counter-reset必须写在父容器上而非标题自身,否则每个标题都会重置计数器导致全显示“1.”;需为每级标题设独立计数器并配合counter-increment与counters()实现嵌套编号。

counter-reset必须放在父容器上,不能写在h1/h2自己身上
很多人一上来就给
写counter-reset: section;,结果每个标题都显示“1.”——因为每个h2都把自己重置了。真正该写counter-reset的地方是它们的共同父容器,比如<article></article>、<main></main>或<div class="content">。<br>这个声明不仅初始化计数器,还划定了作用域:子元素的<code>counter-increment只在这个范围内累加。
如果页面有多个独立章节(比如不同<article></article>),每个都会从 1 开始编号,互不影响。h2和h3要分别控制increment和reset才能生成“1.1”“1.2”这类嵌套编号
单靠一个计数器无法表达层级关系。必须为每级标题配独立计数器,并用counter-reset在父级触发子级重置:
- h1触发chapter递增
- 每个h2既要递增section,又要重置subsection
- h3只递增subsection,不碰section
然后用counters()拼接,而不是手动拼counter(chapter)和counter(subsection)——后者在结构变动时极易错位。
典型写法:
h1 { counter-increment: chapter; }
h2 { counter-increment: section; counter-reset: subsection; }
h3 { counter-increment: subsection; }
h1::before { content: counter(chapter) ". "; }
h2::before { content: counter(chapter) "." counter(section) " "; }
h3::before { content: counters(chapter, ".") "." counter(subsection) " "; }
content里漏引号或写错函数名,编号就完全不显示
content必须配合伪元素(::before或::after)使用,且字符串拼接规则严格:
- content: counter(section) ". " ✅
- content: counter(section) . ❌(点号没引起来,语法错误)
- content: counters(section, ".") ✅(注意是复数counters,不是counter)
- content: counter(section, ".") ❌(counter()不接受第二个参数)
中文前缀如“第”可直接拼: content: "第" counter(section) "章";,但中文后空格建议用\a0避免被CSS压缩。
编号从几开始?别搞混counter-reset的值和实际显示值
counter-reset: section 5;意思是“从 5 开始”,但第一个h2触发counter-increment: section;后,显示的是 6 —— 因为increment是在reset之后执行的。
想从 1 开始,最简写法是counter-reset: section;(默认就是 0);
想跳过编号,可用counter-increment: section 2;让每次+2;
想临时禁用某项编号,给它加counter-increment: none;,比display:none更干净——DOM还在,只是不参与计数。
编号逻辑依赖DOM顺序和选择器匹配,不是“渲染时看谁在上面”,而是“解析HTML时按节点位置逐个触发”。一旦结构嵌套变深或选择器范围过大,容易意外重置或漏计,得靠浏览器开发者工具的Computed面板逐层检查counter()值,而不是凭感觉调样式。











