必须先用counter-reset声明计数器,再用counter-increment递增,最后用::before+content显示;嵌套编号需配合counters()和每层counter-reset,否则编号错乱或不显示。

为什么只写 counter-increment 没反应
浏览器根本不知道这个计数器从哪来,counter-increment: section; 只是“让某个变量加 1”,但没声明过 section 这个变量,它就不存在。常见表现是编号全显示为 0 或完全不出现。
必须在父容器(比如 <article></article>、<main></main> 或最外层 <div class="content">)上先写:<br><code>counter-reset: section;
- 这个声明不仅初始化计数器,还定义了作用域——子元素的
counter-increment只会在这个范围内累加 - 别把
counter-reset写在h2上,否则每个h2都重置成 1,永远编不了号 - 计数器名区分大小写:
section和Section是两个不同计数器
h2 自动编号要三步一起走
编号不是自动贴上去的,得靠 ::before + content 把值“拉出来”。漏掉任何一步,页面都看不到数字。
典型写法:
article { counter-reset: section; }
h2 { counter-increment: section; }
h2::before { content: counter(section) ". "; }
-
counter(section)输出阿拉伯数字,从 1 开始(因为第一次increment后读取) - 引号不能省:
content: counter(section) ". "有效,content: counter(section) .直接失效 - 如果想从第 3 章开始,把
counter-reset: section 2;(注意是 2,不是 3) - 前缀如“第 X 章”可写成:
content: "第" counter(section) "章";,中文后空格建议用\a0避免被压缩
嵌套编号用 counters() 而不是拼 counter()
手动写 counter(section) "." counter(subsection) 容易错位:当 h3 出现但对应 h2 没变时,counter(section) 还是旧值,编号就乱了。
正确做法是靠 DOM 嵌套关系自动回溯:
h2 { counter-increment: section; counter-reset: subsection; }
h3 { counter-increment: subsection; }
h3::before { content: counters(section, ".") ". "; }
-
counters(section, ".")会沿着父级向上找所有名为section的计数器,按层级拼出1.2.3 - 第二个参数必须是字符串字面量,
"."有效,var(--sep)或.(没引号)都不行 - 如果
h2下直接放h4,h4不会继承h3的计数上下文——层级断了,counters()就停在h2那一级
编辑器里用 CSS 编号要防干扰
富文本编辑器(如 Tiptap、Slate)生成的 li 或标题常带内联样式或光标逻辑,::before 内容若没约束好,可能被当成可编辑文本,导致回车后编号重复、光标跳位。
- 限定作用域:
.editor-content ol li::before比ol li::before更安全 - 加
user-select: none;防止用户意外选中编号 - 避免设
display: block或固定宽高,否则可能挤压实际文本区域 -
white-space: nowrap;防止编号中间被换行断开(比如 “1.” 和 “标题” 分两行)
多级列表尤其容易串号,关键不在怎么写 content,而在每一层 ol 是否都有自己的 counter-reset —— 漏掉一层,整个嵌套就塌掉。











