<p>Bootstrap 原生类可组合实现真正响应式 Steps:用 nav + progress + 网格系统分层控制,nav-link 承载标题与状态,col-* 大屏横排、flex-column 小屏竖排,箭头用 border-right + flex-grow 动态撑开,状态由 aria-current 属性驱动(如 aria-current="step"),禁用 .disabled 改用 tabindex="-1" + pointer-events: none,小屏重置 gap 和 padding,::after 画箭头需排除最后一项,嵌表单时每步用 fieldset + legend 保障键盘导航,状态同步需 JS 手动更新 aria-current。</p>
用 bootstrap 原生类写响应式 Steps,别碰 JS
bootstrap 本身没有叫 steps 的内置组件,但能用 nav + progress + 网格系统组合出真正响应式的步骤条——关键是放弃“一步封装完”的想法,接受分层控制。
常见错误是硬套 list-group 或强行给 step 加 position: absolute,结果在小屏上文字重叠、箭头错位、点击区域消失。
- 用
nav做语义化结构,nav-link承载每步标题和状态 - 用
col-*控制大屏横向排列,用flex-column在移动端自动转竖排 - 箭头不用图片或伪元素硬写,改用
border-right+flex-grow动态撑开,避免断点时错位
step 状态样式必须靠 aria-current 驱动,不是 class
很多人加 class="active" 或 class="completed",结果屏幕阅读器读不出当前步,也不符合 Bootstrap 5+ 的推荐实践。正确做法是让状态由属性驱动,CSS 用属性选择器匹配。
比如:<li class="nav-item"><a href="#" aria-current="step">填写信息</a></li>,然后写 CSS:[aria-current="step"] { font-weight: 600; color: #0d6efd; }
-
aria-current="step"表示进行中,"true"表示已完成(部分场景),不能混用 - 别用
.disabled控制未激活步,它会禁用焦点和键盘导航;该用tabindex="-1"+pointer-events: none - 如果后端渲染,确保
aria-current属性服务端输出准确,否则 React/Vue 客户端 hydrate 时容易错乱
小屏下垂直堆叠时,gap 和 padding 要重置
横向步骤条在 col-12 col-md-3 下表现正常,但一到手机屏,flex-column 后若不调 gap,上下步间距会过大,箭头线也会被拉长变形。
直接在媒体查询里重设更稳:@media (max-width: 576px) { .step-nav { gap: 1rem; } .step-nav .nav-link { padding: 0.75rem 1rem; } }
- 别依赖 Bootstrap 的
gap-3这类响应式工具类,它们在 flex column 下对齐逻辑不稳定 - 箭头线建议用
::after画,但只在非最后一项上生效:.nav-item:not(:last-child) .nav-link::after,避免底部多一条线 - 字体大小在移动端建议降到
0.875rem,否则nav-link高度撑开导致行距失衡
和 form 混用时,fieldset 和 legend 别漏掉
如果步骤条嵌在多步表单里(比如注册流程),光有视觉步骤不够,还得让每个步骤块成为可聚焦的语义单元。否则键盘用户 tab 到第一步后,按 Shift+Tab 会跳过整个步骤导航。
正确结构是:每个 step 包在 fieldset 里,用 legend 包住 nav-link 文字,并设 aria-hidden="true" 防止重复朗读。
-
fieldset本身要加tabindex="0",不然无法键盘进入 -
legend内容需和nav-link文本一致,保持一致性 - 不要把整个步骤条包进一个
fieldset,那会让所有步骤变成一个 tab stop
aria-current 和真实步骤不一致。这块没法靠 CSS 解决,得在状态变更时手动更新属性,而且得等 DOM 更新完成再操作。


















