配了多年 CSS 计数器,每次子组件一加内容整个页面的编号就乱了——今天 Safari 27 把这件事用一行属性彻底原生化了

你在一个卡片组件里写了 counter-reset: section,结果整个页面的目录编号全乱了——这种事配 CSS 的人大概都遇到过。根本原因不是你的命名没做好,而是 CSS 计数器本身是全局的,任何子树的 counter-increment 都可能影响到文档其他位置。

Safari 27(2026-09-18)给 contain: style 补上了最后一块拼图:现在这个属性可以真正把引号和计数器封在一个子树里,不让它们泄漏到外部。

一、什么是 style containment

contain 是 CSS 用来告诉浏览器「这个元素的子树和页面其余部分是独立的」的属性。之前已经有三种 containment 类型:

  • layout——子元素不影响外部 layout
  • paint——子元素被裁剪到自身边框内
  • 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-resetquotes 的使用位置,优先给那些被多个地方引用的组件加上 contain: style,避免编号冲突。

Safari 27 全球覆盖率目前约 96%,可以渐进增强使用——不支持的浏览器会直接忽略 contain 声明,不影响现有功能。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 69 阅读