最合理的选择是+(强时序)或(含分支),配aria-current="step"和aria-label;连接线用li:not(:last-child) > span::after实现,确保语义清晰、无障碍可用。

订单状态流程该用什么HTML元素
不能用 <div> 堆砌步骤条,也不能强行套用 <code><ol></ol>(除非状态严格线性、不可跳转且需序号)。最合理的语义化选择是 <nav></nav> + <ol></ol> 或 <ul></ul>,外加明确的 aria-current 和 aria-label。因为订单状态本质是导航路径——用户需要感知“当前在哪”“前面走过了什么”“后面还有哪些节点”,这正是 <nav></nav> 的语义职责。
为什么 <ol></ol> 比 <ul></ul> 更合适
订单状态有强时序性和不可逆性(如:待支付 → 已支付 → 配送中 → 已签收),<ol></ol> 天然表达顺序关系,对屏幕阅读器更友好,且 CSS 伪元素 ::marker 可控性更强。但要注意:
- 如果后端可能插入“已取消”或“异常挂起”等非线性分支状态,就别依赖
<ol></ol>的自动计数,改用<ul></ul>+ 手动data-step控制视觉序号 -
<ol></ol>默认从 1 开始,若状态从“已下单”开始,不要手动设start="0"—— 语义上“第 0 步”不成立,应把“已下单”作为第一步 - 避免嵌套
<ol></ol>表示子状态(如“配送中”下分“打包”“出库”“在途”),这会破坏主流程语义;子状态应另起结构或用<details></details>
aria-current 必须加在当前状态项上
这是屏幕阅读器识别“现在走到哪了”的唯一可靠依据。只靠 CSS 类名(如 .active)或颜色变化,对无障碍完全无效。正确写法:
注意:
- 值必须是
"step",不是"page"或"location"—— 这是 WAI-ARIA 规范明确定义的流程步进类型 - 不能加在
<ol></ol>或<nav></nav>容器上,必须精准落在当前<li> - 服务端渲染时,务必确保生成的 HTML 中只有一个
aria-current="step",前端 JS 切换状态时要同步增删该属性,不能只改 class
CSS 绘制连接线时绕不开的 DOM 结构陷阱
常见错误是用 <li> 的 ::after 画右向横线,结果最后一步也多出一条线。根本原因是没隔离“连接线”和“节点”的语义。推荐结构:
<nav aria-label="订单状态流程"><ol>
<li><span>待支付</span></li>
<li aria-current="step"><span>已支付</span></li>
<li><span>配送中</span></li>
</ol></nav>
然后用 CSS 选中非末尾项的 <li> 来画线:
ol > li:not(:last-child) > span::after {
content: "";
display: inline-block;
width: 24px;
height: 2px;
background: #ccc;
margin: 0 8px;
}
这样既语义清晰,又避免 JS 计算节点数量或动态增删 DOM。
真正难的不是画线,而是当状态支持“回退”(如客服操作退回至“待支付”)或“并行分支”(如“部分发货”)时,DOM 结构是否还能保持可读、可访问、可维护。这时候,硬套一个“步骤条组件”反而会掩盖语义断裂的问题。











