应使用语义化结构,以aria-current="step"标记当前步、aria-current="true"标记已完成,配合伪元素生成图标和连线,确保可访问性与响应式一致性。

用 nav + aria-current 搭语义化结构,别碰 .progress
Bootstrap 官方没有 .steps 或 .step-bar,硬套 .progress 会破坏可访问性——屏幕阅读器只读“进度 40%”,不是“第二步”,也无法聚焦每项。必须用 <nav aria-label="表单步骤"><ol></ol></nav> 包裹步骤,每项是 <li class="nav-item"><a href="#step-2"></a></li>,靠 aria-current="step" 标记当前步(不是 class="active"),aria-current="true" 表示已完成。
::before 生成图标,状态由属性驱动
图标不能插 DOM 或用绝对定位,否则小屏错位、文字折行后偏移。统一用伪元素:
-
.nav-link::before设基础尺寸:content: ""、display: inline-block、width: 1.25rem、height: 1.25rem、border-radius: 50%、vertical-align: middle - 当前步:
[aria-current="step"]::before { background-color: #0d6efd; } - 已完成:
[aria-current="true"]::before { content: "✓"; color: #198754; font-weight: bold; }(注意:别混用"true"和"step") - 未开始:
:not([aria-current])::before { content: counter(step); }配合counter-reset: step;自增编号 - 移动端字体缩到
font-size: 0.875rem,防止nav-link高度撑开导致图标基线偏移
连线用 border-right + flex-grow,别写死宽度
横排时连线随步骤数自动延伸,竖排时转为 border-bottom,全靠 CSS 控制,不依赖 JS 计算宽度:
- 给非末项的链接加
border-right: 2px solid #e9ecef,再设flex-grow: 1帮它填满剩余空间 - 只在非最后一项画线:
.nav-item:not(:last-child) .nav-link,避免底部多一条线 - 小屏竖排时:
@media (max-width: 576px) { .step-nav { flex-direction: column; } .nav-link::before { margin-right: 0; margin-bottom: 0.5rem; } .nav-link { padding: 0.75rem 1rem; } } - 禁用未激活项时,用
tabindex="-1"+pointer-events: none,别用disabled——后者会砍掉键盘焦点
切换前必须校验,不能只改 UI 状态
点“下一步”只更新 aria-current 是典型陷阱:UI 走到第三步,但第二步邮箱字段为空,后端收不到完整数据。
- 点击事件里必须调
checkStepValidity(),不是等最终submit时才校验 - 校验失败时,用
input.focus()定位到第一个无效字段,别只弹alert - “下一步”按钮要同步禁用:
nextBtn.disabled = true,别只加opacity: 0.5 - 异步校验(如用户名可用性)完成前,按钮进 loading 状态,并防重复点击
- 服务端渲染时,确保
aria-current属性由后端准确输出,否则 React/Vue hydrate 易错位
nav 的 flex-direction 切换严格同步,稍有延迟或遗漏,小屏下就会出现图标错位、连线断裂、文字重叠——这些都不是样式问题,而是语义结构与状态驱动逻辑没对齐。











