根本原因是 Safari iOS 对伪元素在 flex/grid 中渲染顺序敏感,且动态插入或 display: none 切换时计数器状态不重置;需在最外层设 counter-reset,步骤项须为块级或 flex 子项,隐藏用 visibility: hidden 而非 transform 或移除 DOM。

为什么 counter-reset 和 counter-increment 在移动端常失效?
根本原因不是 CSS 不支持,而是移动端浏览器(尤其是 Safari iOS)对伪元素在 flex 或 grid 容器中的渲染顺序敏感,且当步骤条节点被动态插入、或父容器使用 display: none 切换时,计数器状态不会自动重置或延续。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 始终在最外层步骤容器(如
.steps)上声明counter-reset: step 0,不要放在:before里 - 每个步骤项(
.step)必须是块级或 flex 子项,不能是display: inline或被transform: scale(0)隐藏——这会导致 Safari 跳过计数器计算 - 避免用 JS 删除/重建 DOM 节点来“重置”步骤;改用
visibility: hidden+opacity: 0配合pointer-events: none隐藏非当前步
如何让序号在小屏下自动缩放又不破坏计数逻辑?
计数器内容(counter(step))本身不响应缩放,但它的容器可以。关键在于把字体大小控制和计数器分离,而不是对 ::before 直接设 font-size: clamp() —— 这在部分 Android WebView 中会触发重绘异常,导致序号错乱。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 给步骤项(
.step)设font-size: clamp(12px, 2.5vw, 16px),再让::before继承:content: counter(step); font-size: 1em; - 如果需要序号圆圈背景自适应,用
aspect-ratio: 1 / 1替代固定宽高,配合min-width: 1.2em防止过小 - 禁用
text-rendering: optimizeLegibility(尤其在 iOS),它会让小字号下的数字 1/7/9 渲染模糊,干扰视觉识别
如何用 CSS counters 实现「已完成 → 当前 → 待办」三态样式切换?
纯 CSS 无法读取计数器数值做条件判断(比如 if counter(step) ),所以必须借助结构特征:当前步通常有 class <code>.is-current,已完成步有 .is-done,其余为待办。计数器只负责生成数字,状态样式靠类名驱动。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 序号数字统一用
counter(step),但颜色/背景靠兄弟选择器控制:.step.is-done::before { background: #4CAF50; color: white; } - 不要尝试用
counter(step, lower-roman)表示待办步——罗马数字在小屏下更难识别,且无语义优势 - 若需「当前步序号放大」,给
.step.is-current::before单独加transform: scale(1.2),并设transform-origin: center,避免偏移
为什么 counter(step, decimal-leading-zero) 在 iOS 上显示为普通数字?
因为 decimal-leading-zero 是 CSS Counter Styles Level 3 特性,Safari 直到 iOS 16.4 才完整支持,旧版 iOS(包括大部分企业内网设备)会直接 fallback 到 decimal。
实操建议:
立即学习“前端免费学习笔记(深入)”;
- 放弃
decimal-leading-zero,改用伪元素叠加实现补零:.step::before { content: '0' counter(step); },再用font-variant-numeric: tabular-nums对齐数字宽度 - 如果必须两位数(如 01/02/03),且步骤数确定 ≤99,可用属性选择器硬编码:
.step:nth-child(1)::before { content: '01'; },虽不灵活但 100% 兼容 - 检查构建工具是否误删了
counter()的括号——某些 CSS 压缩器(如 cssnano v4)会错误处理带逗号的函数参数
真正麻烦的不是写几行 counter-increment,而是当步骤由 React/Vue 动态渲染、又叠加了路由懒加载和骨架屏时,CSS 计数器的状态与 DOM 生命周期不同步。这时候宁可多写几行 JS 来管理 data-step-index,也别赌浏览器能正确维护跨 Fragment 的计数器链。


















