CSS counters可实现合同条款的多级嵌套编号(如1→1.1→1.1.1),通过counter-reset、counter-increment和counters()配合控制层级,无需JS,保障打印、PDF导出及屏幕阅读器兼容性。

ol 和 li 本身不支持自动嵌套编号格式(如“1.1.2”),直接写多层 ol 会变成“1 → 1 → 1”,不是合同需要的层级编号。
用 CSS counters 实现真正的条款分级(如 1 → 1.1 → 1.1.2)
原生 HTML 的 ol 只能做线性计数,合同条款要求语义化 + 可继承编号,必须靠 CSS counter-reset 和 counter-increment 控制。
- 外层
ol设counter-reset: section,li::before显示counter(section) - 子
ol设counter-reset: subsection,且父li要设counter-increment: section - 每级
li::before拼接上一级计数器,例如counter(section) "." counter(subsection) - 注意:不能依赖
type属性或start——它们不跨层级,也无法生成小数点分隔符
为什么不用 JavaScript 动态写编号
合同文本需打印、存档、PDF 导出,且常被法务/审计方直接查看源 HTML。JS 注入编号在无运行环境时失效,innerHTML 修改还会破坏 DOM 语义结构。
- PDF 工具(如 wkhtmltopdf、WeasyPrint)默认不执行 JS,编号会丢失
- 屏幕阅读器依赖原生
ol/li结构播报层级,JS 插入的文本不被识别为序号 - 一旦需要调整某条款顺序,JS 重排逻辑极易出错;CSS counters 是声明式、自动重算的
实际写法示例:三级条款(1 → 1.1 → 1.1.1)
<style>
ol { counter-reset: section; }
li { counter-increment: section; }
li::before { content: counters(section, ".") ". "; }
ol ol { counter-reset: subsection; }
ol ol li { counter-increment: subsection; }
ol ol li::before { content: counters(section, ".") "." counters(subsection, ".") ". "; }
ol ol ol { counter-reset: subsub; }
ol ol ol li { counter-increment: subsub; }
ol ol ol li::before { content: counters(section, ".") "." counters(subsection, ".") "." counters(subsub, ".") ". "; }
</style>
<ol>
<li>甲方义务</li>
<li>乙方义务
<ol>
<li>交付范围</li>
<li>验收标准
<ol>
<li>性能指标</li>
<li>文档清单</li>
</ol>
</li>
</ol>
</li>
</ol>
关键点:所有编号逻辑由 CSS 驱动,HTML 保持干净;counters() 第二个参数是连接符,必须是字符串字面量(如 "."),不能是变量或表达式。
立即学习“前端免费学习笔记(深入)”;
容易漏掉的是 counter-reset 必须在每一级 ol 上显式声明,否则子级不会从 1 开始计数;另外,如果条款里混有非 li 元素(如 p 或注释),会影响 counter-increment 的触发时机——只对 li 生效。



















