counter-increment 必须作用于真实 dom 元素,伪元素(::before/::after)上设置无效;display: none 元素不触发计数;counter-reset 需置于真实父容器;counters() 分隔符必须加引号。

counter-increment 写在 ::before 里根本不会触发
伪元素本身不能递增计数器,counter-increment 必须作用于真实 DOM 元素(比如 <h2></h2>、<li>、<div class="item">),而不是 <code>::before 或 ::after 选择器。写在伪元素上等于没写——浏览器直接忽略,不报错也不计数。
常见错误示例:
h2::before {
counter-increment: section; /* ❌ 无效!伪元素不参与计数器递增 */
content: "Section " counter(section);
}
正确做法是把 counter-increment 移到真实元素上:
h2 {
counter-increment: section; /* ✅ 在 h2 元素上触发 */
}
h2::before {
content: "Section " counter(section) ": "; /* ✅ 仅负责显示 */
}
display: none 的元素上 counter-increment 失效是必然的
display: none 会把元素彻底移出渲染树,浏览器连它是否存在都不知道,counter-increment 自然不会执行。这不是兼容性问题,是 CSS 规范定义的行为。
如果你需要“隐藏但继续计数”,必须换用 visibility: hidden:
-
visibility: hidden保留元素在渲染树中,计数器可正常累加 - 若同时想让编号可见,可在伪元素里显式设
visibility: visible - 注意:它仍占文档流位置,可能影响布局;如需完全不占空间,得配合
position: absolute或width: 0; height: 0等 hack,但已超出纯 CSS 计数器设计初衷
counter-reset 没生效?检查作用域锚点是否在真实父容器上
counter-reset 不继承、不传播、不穿透伪元素边界。它只对「后续同级及后代中满足匹配条件」的 counter-increment 生效。典型失效场景:
- 把
counter-reset: step;写在::before里 → 对外部<li class="step-item">完全无效 - 父容器用了
display: contents或contain: layout→ 中断计数器上下文 - 在
flex/grid中靠order改变视觉顺序 → 计数仍按 DOM 顺序走,容易误判为“重置失败”
可靠写法:把 counter-reset 明确放在最外层真实容器上,例如:
一款AI工具,主要用于在主代理响应前,并行运行Kimi K2.5和GPT 5.3 Codex,注入双方观点以增强认知多样性,适合需要提升相关任务效率的用户。
.steps { counter-reset: step; }
.steps .step-item { counter-increment: step; }
.steps .step-item::before { content: "Step " counter(step) ": "; }
content 里用错了 counters() 的分隔符语法
counters() 的第二个参数必须是带引号的字符串字面量,比如 "." 或 "–"。写成 .(无引号)、var(--sep) 或 attr(data-sep) 都会解析失败,导致整个 content 不渲染。
错误写法:
h3::before {
counter-increment: section subsection;
content: counters(section, .) "." counter(subsection); /* ❌ 解析失败 */
}
正确写法:
h3::before {
counter-increment: section subsection;
content: counters(section, ".") "." counter(subsection); /* ✅ */
}
另外注意:counters() 只在嵌套结构中起作用,前提是每一级都设置了对应 counter-increment 和层级化的 counter-reset,否则只会返回最内层值。
真正卡住人的地方往往不是语法写错,而是误以为“写了就该生效”——CSS 计数器依赖渲染树存在、作用域锚点明确、伪元素仅负责显示。只要节点不在渲染树里(display: none)、或递增动作没落在真实元素上、或重置没锚定到有效父容器,它就静默失败,连 warning 都不给。










