配了多年 CSS 计数器,每次子组件一加内容整个页面的编号就乱了——今天 Safari 27 把这件事用一行属性彻底原生化了
你在一个卡片组件里写了 counter-reset: section,结果整个页面的目录编号全乱了——这种事配 CSS 的人大概都遇到过。根本原因不是你的命名没做好,而是 CSS 计数器本身是全局的,任何子树的 counter-increment 都可能影响到文档其他位置。
Safari 27(2026-09-18)给 contain: style 补上了最后一块拼图:现在这个属性可以真正把引号和计数器封在一个子树里,不让它们泄漏到外部。
一、什么是 style containment
contain 是 CSS 用来告诉浏览器「这个元素的子树和页面其余部分是独立的」的属性。之前已经有三种 containment 类型:
layout——子元素不影响外部 layoutpaint——子元素被裁剪到自身边框内size——子元素不影响自身尺寸计算
Safari 27 新增的是 style,作用是让 counter-* 和 quotes 属性创建的计数器/引号只作用于当前子树,不影响文档其余位置。
.card {
contain: style;
counter-reset: card-section;
}
加了这一行之后,card 内部的 counter-increment: card-section 就不会影响到页面头部的目录编号了。引号同理——open-quote/close-quote 生成的引号也只在该子树内部可见。
二、为什么之前用不了
CSS Containment Level 2 规范里,contain: style 一直是”at-risk”状态——属于可选支持的特性,浏览器可以自己决定是否实现。Chrome 从 52 开始支持,但只支持了计数器,不支持引号;Safari 在 15.4 支持了 contain: style,但同样不支持引号(WebKit bug 232083)。
Safari 27 修复了这个历史遗留问题,现在 contain: style 在 Safari 里同时对计数器和引号生效。
三、和 Shadow DOM 的区别
有人可能会想:这不是 Shadow DOM 的工作吗?contain: style 不是 scoped styles,它不会创建样式隔离(class 和 id 选择器仍然会跨边界匹配)。它只隔离计数器和引号的行为,不隔离其他 CSS 规则。
四、落地方案
/* 需要隔离计数器的组件 */
.component {
contain: style;
counter-reset: local-counter;
}
/* 需要隔离引号的组件 */
.blockquote {
contain: style;
quotes: "«" "»";
}
如果你不确定哪些组件需要加,可以先在全局搜一下 counter-reset 和 quotes 的使用位置,优先给那些被多个地方引用的组件加上 contain: style,避免编号冲突。
Safari 27 全球覆盖率目前约 96%,可以渐进增强使用——不支持的浏览器会直接忽略 contain 声明,不影响现有功能。
评论区
登录后可评论。