最可控方案是counter-reset配合::before;::marker样式能力弱且旧Safari支持差;list-style-type仅控制预设类型,无法自定义外观或添加前缀。

直接用 counter-reset 配合 ::before 是目前最可控、兼容性最好、能自由加样式(背景、圆角、颜色等)的方案;::marker 虽语义更正,但样式能力弱、旧 Safari 支持差,且不适用于 ul。
为什么不能只靠 list-style-type?
list-style-type 只控制“类型”,不是“外观”。它只能选预设值(如 decimal、lower-roman),没法改颜色、加背景、设圆角、调字体大小,也不能插入前缀(比如 “Step 1.”)或图标。一旦你写了 list-style-type: none,原生序号就彻底消失——这是刻意清场,为后续自定义腾位置。
counter-reset + ::before 的实操要点
这是真正落地时最常写的组合,关键在四步不漏:
- 给父容器(如
ol)设counter-reset: step;—— 否则counter-increment没基础计数器可增 - 给每个
li设counter-increment: step;—— 注意:这句必须写在li上,不能只写在::before里 -
li::before必须有content: counter(step) ". ";—— 漏掉content,序号完全不显示 - 若要定位序号(比如左对齐、缩进),
li得先设position: relative;,::before才能用position: absolute;精确摆放
示例片段:
立即学习“前端免费学习笔记(深入)”;
ol.custom { counter-reset: step; }
ol.custom li { position: relative; margin-left: 2em; }
ol.custom li::before {
counter-increment: step;
content: counter(step) ". ";
position: absolute;
left: -2em;
width: 2em;
text-align: right;
color: #3a7ebf;
font-weight: bold;
}
::marker 的真实能力边界
别被“伪元素”名字误导:::marker 不是通用样式钩子,它只接受极少数属性:
- 支持:
color、font-family、font-size、font-weight、content - 不支持:
background、padding、margin、border、display、transform -
content里只能用counter()或字符串,不能用attr(),也不能嵌套其他伪元素 - Firefox 目前不支持
content: counter(...)(仅 Chrome/Edge/Safari 15.4+ 支持) -
ul的圆点/方块无法用::marker自定义形状,仍得靠background-image或list-style-image
容易踩的坑和隐性细节
最常卡住人的不是语法,而是这些细节:
-
counter-reset名字必须和counter-increment、counter()里用的一致,大小写敏感,拼错就全失效 -
ol li::marker { content: "▶ " counter(list-item) ". "; }中的list-item是ol默认计数器名,但自定义counter-reset后就得用你起的名字(如step) -
content里空格要手动加,"Step"counter(step)会变成Step1,得写成"Step " counter(step) ". " - 用
::before时,若li是display: inline,::before默认是块级,需加display: inline-block对齐 - 嵌套列表要用
counters(step, "."),单用counter(step)只出顶层数字(如 1、2,而非 1.1、1.2)
真正难的不是写出来,是判断该用 ::marker 还是 ::before:前者省心但受限,后者灵活但要补语义(比如加 role="listitem" 和 aria-hidden="true")。


















