学完这篇你能得到一份同时适配 Chrome/Safari/Edge 和 Firefox 的自定义滚动条代码,不会再出现"改完只有一半浏览器生效"的情况。
第一步:先分清两套 API
滚动条样式目前有两套并行标准,互不通用:
- WebKit 私有伪元素:
::-webkit-scrollbar、::-webkit-scrollbar-thumb、::-webkit-scrollbar-track,Chrome、Safari、Edge、国产套壳浏览器(基本都是 Chromium 内核)都认它。能改宽度、圆角、颜色、悬停态,能力最强。
- W3C 标准属性:
scrollbar-width 和 scrollbar-color,Firefox 支持,Chromium 121+ 也开始支持。只能改粗细(auto / thin / none)和两个颜色,改不了圆角。
所以正确的思路是:主力用 WebKit 伪元素,Firefox 用标准属性兜底,而不是把两套属性无脑堆在一起。
第二步:写 WebKit 那套
.scroll::-webkit-scrollbar {
width: 8px; /* 纵向滚动条宽度 */
height: 8px; /* 横向滚动条高度,别漏 */
}
.scroll::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.scroll::-webkit-scrollbar-thumb {
background: #b0b0b0;
border-radius: 4px;
}
.scroll::-webkit-scrollbar-thumb:hover {
background: #888;
}
.scroll::-webkit-scrollbar-corner {
background: transparent; /* 横竖滚动条交汇的小方块 */
}
注意:这几个伪元素必须拆开写。::-webkit-scrollbar 里的 width/height 只对它自己生效,把 width 写进 ::-webkit-scrollbar-thumb 是无效的,这是最常见的白写原因。
第三步:给 Firefox 写标准属性
.scroll {
scrollbar-width: thin;
scrollbar-color: #b0b0b0 #f1f1f1; /* 滑块色 轨道色 */
}
scrollbar-color 只接受两个颜色值,顺序是「滑块 轨道」,写反了就是一片糊。想隐藏滚动条用 scrollbar-width: none。
第四步:把两套隔开,避免互相覆盖
这是最容易踩的坑:Chromium 121 之后,只要你给元素设了 scrollbar-width 或 scrollbar-color,浏览器就切到标准渲染路径,之前写的 ::-webkit-scrollbar 圆角和宽度会全部失效。所以不能两套直接并列,要用 @supports 做互斥:
/* 默认走 WebKit */
.scroll::-webkit-scrollbar { width: 8px; height: 8px; }
.scroll::-webkit-scrollbar-thumb { background: #b0b0b0; border-radius: 4px; }
.scroll::-webkit-scrollbar-track { background: #f1f1f1; }
/* 只有不支持 WebKit 伪元素的浏览器(Firefox)才吃到标准属性 */
@supports not selector(::-webkit-scrollbar) {
.scroll {
scrollbar-width: thin;
scrollbar-color: #b0b0b0 #f1f1f1;
}
}
这样 Chrome 永远不会拿到标准属性,圆角和宽度就能保住;Firefox 走 @supports not 分支,也有样式。
第五步:处理几个高频场景
隐藏滚动条但保留滚动能力:
.hide-scroll { scrollbar-width: none; -ms-overflow-style: none; }
.hide-scroll::-webkit-scrollbar { display: none; }
注意:-ms-overflow-style 是给旧版 Edge/IE 的,虽然现在用不上,但加上不影响。
深色模式:把颜色抽成 CSS 变量,在 @media (prefers-color-scheme: dark) 里换一套值即可,两套选择器都引用同一个变量,不用重复写。
滚动条宽度变化导致页面横向跳动:加 scrollbar-gutter: stable; 预留位置,避免内容在有无滚动条之间来回抖。
注意:iOS Safari 的滚动条是系统悬浮式、默认淡出,自定义基本看不到效果;移动端不要指望滚动条样式,该用视觉提示的地方老老实实做。另外,给整页改滚动条时,样式写在 html 或 body 上在不同浏览器里归属不一致,建议先写 html 再补 body 双保险。
小结
- 两套 API:WebKit 伪元素管细节(宽度、圆角、悬停),标准属性管 Firefox 保底。
- 伪元素要逐个声明,
width 只写在 ::-webkit-scrollbar 上。
- 两套属性别并列,用
@supports not selector(::-webkit-scrollbar) 隔离,防止新 Chromium 覆盖掉圆角。
scrollbar-color 顺序是「滑块 轨道」,只能填颜色。
- 隐藏滚动条要三条一起写;布局跳动用
scrollbar-gutter: stable;移动端别抱期望。