不能包住所有流程步骤,因其语义仅适用于主导航;流程导航应使用包裹步骤项,配合aria-current="step"标识当前步,并用定义容器语义。

为什么 <nav></nav> 不能包住所有流程步骤
企业级流程导航常被误当成普通菜单,直接用 <nav></nav> 套整个步骤条。但语义上,<nav></nav> 仅适用于「主导航」——即跳转到不同页面或大区块的链接集合。而流程导航(如「填写→审核→发布」)本质是当前页面内的状态进度,属于 <section></section> 或 <ol></ol> 的范畴。
实操建议:
- 用
<ol></ol>包裹步骤项,天然表达顺序性与完成状态(配合aria-current="step") - 每个步骤用
<li>,内部用<span></span>或<strong></strong>标记当前步,避免滥用<a></a>(未完成步不应可点击) - 若某步支持跳转(如返回编辑),再加
<a></a>,并设tabindex="-1"禁用键盘焦点(除非已激活)
aria-current="step" 比 CSS 类更可靠
仅靠 class="active" 无法被屏幕阅读器识别为「当前步骤」。必须用 aria-current="step" 才能触发读屏软件播报“当前:审核中”。
常见错误现象:
- 写了
aria-current="true"—— 无效值,只接受"page"、"step"、"location"等明确语义值 - 在非
<li>元素上使用(如套在<div> 外层)—— 应直接落在代表该步骤的 <code><li>或<a></a>上 - 服务端渲染时漏传属性,导致首屏无
aria-current—— 需确保 SSR 输出包含该属性,而非仅靠 JS 补充 - 外层用
<section></section>,配aria-label="发布流程"(比空<div> 强) <li>内部第一个子元素必须是 <code><h2></h2>,文本如“内容发布流程”,不可省略或用<span></span>替代 - 避免嵌套
<nav></nav>在<section></section>内 —— 除非该流程页本身还带独立的次级导航(如“切换模板”“查看历史”) - HTML 结构始终用
<ol></ol>+<li>,CSS 控制显示方式(flex/grid/scroll-snap) - 移动端不隐藏未完成步骤,而是用
opacity: 0.5+pointer-events: none,保持 DOM 存在和语义完整 - 禁用
display: none或visibility: hidden隐藏步骤 —— 屏幕阅读器会跳过,造成流程断裂感
用 <section></section> + <h2></h2> 定义流程容器语义
整个流程导航不是孤立 UI 组件,而是页面主内容的一部分。直接丢在 <header></header> 或 <aside></aside> 里会破坏文档大纲。
正确结构:
响应式断点影响语义,但不改变 HTML 结构
流程步骤在移动端常折叠为横向滚动或下拉展开,但很多人因此改用 <details></details> 或动态插入 <nav></nav>,反而破坏一致性。
关键原则:
最易被忽略的是:流程状态变更(如从「填写」进入「审核」)必须同步更新 aria-current 和 tabindex,且需在 DOM 更新后触发 focus() 到新当前步(如果策略允许键盘操作)。否则,键盘用户会卡在旧位置,而读屏器无法感知变化。











