响应式步骤条应使用 nav + flex-column + 断点类实现,语义结构为 nav > li.nav-item > a.nav-link,状态由 aria-current 属性驱动,箭头用 ::after 伪元素控制,移动端需重置 gap 和字体大小以确保对齐与可访问性。
直接用 nav + flex-column + 断点类就能实现,不用 js、不依赖第三方组件,但必须放弃“一步到位”的封装幻想——响应式步骤条本质是布局逻辑切换,不是样式开关。
为什么不能直接用 .steps 或 .list-group
Bootstrap 官方没有 .steps 组件,硬套 .list-group 会导致移动端文字换行错乱、箭头伪元素定位漂移、点击区域塌陷;.list-group-item 默认是块级堆叠,但缺少语义状态驱动(比如当前步该由 aria-current 控制,而非 class="active"),屏幕阅读器无法识别流程进度。
- 伪元素画的箭头在小屏下容易拉长变形,尤其当内容高度不一致时
-
.list-group的border和padding在 flex column 下对齐逻辑不稳定 - 服务端渲染时若没同步输出
aria-current,客户端 hydrate 后状态脱节
正确结构:用 nav 做语义容器,nav-link 承载每步
每步必须包裹在 li.nav-item 里,a.nav-link 作为可聚焦入口,状态靠属性驱动:
<nav class="nav step-nav"><li class="nav-item">
<a class="nav-link" href="#step1" aria-current="step">填写信息</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#step2">确认订单</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#step3">支付完成</a>
</li>
</nav>
-
aria-current="step"表示进行中,aria-current="true"表示已完成(部分场景),不可混用 - 禁用未激活项时,用
tabindex="-1"+pointer-events: none,别用.disabled(它会禁用键盘导航) - 移动端垂直排列靠
flex-column,但必须加在nav上,不是ul或父容器
断点控制:从横向到纵向的 CSS 切换关键点
大屏横向靠 flex-row + col-* 控制宽度,小屏转竖排必须重置 gap 和 padding,否则间距爆炸、箭头线拉长:
@media (max-width: 576px) {
.step-nav {
display: flex;
flex-direction: column;
gap: 1rem; /* 不要用 gap-3 这类工具类,flex column 下行为不稳定 */
}
.step-nav .nav-link {
padding: 0.75rem 1rem;
font-size: 0.875rem; /* 防止高度撑开导致行距失衡 */
}
.step-nav .nav-item:not(:last-child) .nav-link::after {
content: '';
display: block;
width: 2px;
height: 1rem;
background: #dee2e6;
margin-top: 0.5rem;
}
}
- 箭头线用
::after画,且只在非最后一项生效,避免底部多一条线 -
gap必须在媒体查询里显式重设,Bootstrap 的响应式gap-*类在flex-column下对齐逻辑不可靠 - 字体大小降到
0.875rem是为了匹配移动端nav-link高度,否则行高失衡
容易被忽略的细节:状态样式必须用属性选择器
很多人写 .nav-link.active { color: #0d6efd; },但这绕过了 aria-current 语义,也不兼容 SSR/CSR 混合渲染。正确写法是:
[aria-current="step"] {
font-weight: 600;
color: #0d6efd;
}
[aria-current="true"] {
text-decoration: line-through;
color: #6c757d;
}
- 所有状态样式都必须基于
aria-current属性,而不是 class —— 这是 Bootstrap 5+ 推荐实践,也是无障碍必需 - 如果后端模板没输出
aria-current,React/Vue 客户端 hydrate 后会出现视觉与语义不一致 - 别在 JS 里手动增删
activeclass,应通过设置aria-current属性来驱动样式和读屏器播报











