全屏布局滚动条一出一进把页面撑歪了,我用了五年 media query,今天 CSS 一行就根治了——scrollbar-gutter 把这个折腾彻底收了
写过全屏后台系统的同学都有这个体验:页面加载一瞬间,滚动条跳出来又缩回去,整个布局「抖」了一下。左边导航宽度明明没变,内容区却莫名其妙被挤窄了几像素。
这不是 CSS 写错了,是浏览器默认行为在作怪。
macOS 默认是 overlay scrollbar,滚动条浮在内容上不占宽度。但 Windows 和 Linux 用户用的是 classic scrollbar,要占一个固定宽度。当页面动态加载内容、切换主题色、或者换用户代理时,滚动条状态一变,classic scrollbar 一出一进,布局就抖了。
这个坑我靠媒体查询和 JS 算了五年,直到 scrollbar-gutter 进 Baseline 2024 才彻底收工。
scrollbar-gutter 的作用是「给滚动条预留槽位」,让这个槽位无论有没有滚动条都存在。用法很简单:
body {
scrollbar-gutter: stable both-edges;
}
stable 关键字的意思是:即使内容没超出容器、不需要滚动,也保留这个 gutter。both-edges 则是左右两侧都留,防止单侧预留导致文字居中偏移。
加了这一行之后,页面渲染流程就变了:浏览器在首次布局时就知道右侧要留 12~17px(取决于系统),不管滚动条出不出、overlay 还是 classic,这个宽度都被提前占用。加载动态内容、切换暗色主题、条件渲染侧边栏——都不会再触发布局抖动。
对比一下几种方案:
媒体查询猜系统 —— 只能按操作系统猜滚动条类型,但用户可以手动开启/关闭 overlay 模式,媒体查询判断不了。
JS 监听 + class 切换 —— 每次动态内容加载完都要检查是否出现滚动条,再加/减对应 padding。逻辑复杂,还容易漏掉异步场景。
scrollbar-gutter: stable both-edges —— 声明即生效,浏览器在布局阶段就把这个宽度锁死,不需要任何运行时判断。
/* 基础用法:全局防抖 */
body {
scrollbar-gutter: stable both-edges;
}
/* 局部用法:只给需要滚动的容器 */
.sidebar {
scrollbar-gutter: stable;
}
/* 暗色主题下更明显,因为暗色 classic scrollbar 对比度更高 */
@media (prefers-color-scheme: dark) {
body {
scrollbar-gutter: stable both-edges;
}
}
这个属性从 Baseline 2024 开始,所有主流浏览器都支持。如果你还在用 macOS、开发体验不到这个问题的严重性,换台 Windows 机器打开你们的全屏后台试试,一眼就知道我在说什么。
下一步:全局搜索项目里有没有靠 JS 手动补滚动条宽度的逻辑,把那坨代码删了,换成这一行。
评论区
登录后可评论。