写过 sticky 头部的人大概都踩过这个坑——吸顶之后想加个阴影,只能再写一段 scroll 监听器去算位置。2026 年 Chrome 133 把这件事用两行 CSS 彻底原生化了,连监听器都不用写。
一个 sticky 吸顶头部,加阴影的思路本来应该很简单:吸住了就加 shadow,没吸住就不加。但问题是 CSS 的 position: sticky 只负责”什么时候该停”,它从来不负责通知你”现在停没停”。所以过去十年全行业都在 scroll 事件里写 window.scrollY > threshold,然后 classList.toggle,然后布局重算,然后你发现加了阴影反而更卡了。
Chrome 133 给了你一个新的容器查询语法,叫 scroll-state()。它的工作方式是:问容器”你现在滚到哪了”,而不是让 JS 自己去算。
具体怎么写。先给祖先容器声明一下它要跟踪滚动状态:
.sticky-header-wrapper {
container-type: scroll-state;
position: sticky;
top: 0;
}
然后用容器查询按状态应用样式:
.main-header {
transition: background 0.2s, box-shadow 0.2s;
background: transparent;
}
/ 吸住了就加背景和阴影 /
@container scroll-state(stuck: top) {
.main-header {
background: white;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
}
}
注意这个 @container 规则里的选择器必须是 .main-header——也就是容器的子元素,不能是容器自己。规范的设计逻辑是:一个元素不能靠自己的状态来给自己下定义,得由父级容器来宣布这个状态。
目前 scroll-state() 支持三个状态查询:
stuck——当一个 sticky 元素真的贴住某个边缘时触发。可以精确到 top/bottom/left/right,或者用逻辑方向 inset-block-start。
snapped——当一个 scroll-snap 容器里的某个子元素对齐到吸附位置时触发。可以检测 block/inline/x/y 轴。
scrollable——当容器在某个方向上还有可滚动内容时触发。比如 scrollable: bottom 为 true,说明底部还有内容没滚出来。
三个状态可以组合查询:@container scroll-state(stuck: top and scrollable: bottom),同时满足两个条件才生效。
这三个状态各自对应一个常见场景:stuck 解决 sticky 头部加阴影、导航栏变色;snapped 解决轮播图分页点自动高亮当前卡片;scrollable 解决「滚动查看更多」提示在滚动到内容底部时自动消失。这三个场景在过去全靠 IntersectionObserver + scroll 事件 + classList.toggle,没有一行能跑在主线程外面的。
scroll-state() 解决了这个问题。浏览器在合成器线程(compositor thread)检测滚动状态,状态变化后才通知渲染进程,而不是每次 scroll 都触发 JS 再算布局。这是结构性差异,不是微优化。
Chrome 144 还新增了 scrolled 查询,能知道滚动方向——最近一次是往上滚还是往下滚(scrolled: top/bottom)。配合这个可以做「下滚隐藏头部、上滚显示头部」的交互,全 CSS 实现,不需要 scroll 事件监听。
不过 scrolled 目前只有 Chrome 144+ / Edge 144+ 支持,Firefox 和 Safari 还没上。
关于兼容性,规范的设计是让你用 @supports 做渐进增强。浏览器不支持的自动跳过 @supports 块,没有 scroll-state 的设备拿到的是 static 样式,不会报错。
最后说一个坑:position: sticky 必须同时有 inset(比如 top: 0)才会真正触发 stuck 状态。如果你只写了 position: sticky 但没有任何 inset 值,浏览器认为这个元素”随时可能动”,不会触发 stuck。这个是规范行为,不是 bug。
下一步:如果你现在项目里有用 scroll 事件加 sticky 阴影的,直接把这个模式迁移过去,container-type + @container scroll-state(),两行 CSS 替代 10-20 行 JS,INP 也会好看一些。生产环境记得加 @supports 兜底,别忘了测 Firefox 和 Safari——它们目前不支持,会看到 static 样式,这是预期行为不是故障。
评论区
登录后可评论。