面包屑必须用 <nav> 包裹并添加 aria-label="Breadcrumb"(中文用 "面包屑导航"),配合 <ol><li> 结构、上级页用 <a>、当前页用 <span aria-current="page">,分隔符需 aria-hidden="true" 或伪元素实现。

面包屑用 nav 包裹是基础,但光这样不够
仅靠 <nav> 无法让屏幕阅读器准确识别这是“面包屑导航”,尤其当页面存在多个 <nav> 时(比如顶部主导航 + 面包屑 + 侧边栏导航)。必须配合 aria-label="Breadcrumb" 或 aria-labelledby 显式声明用途。
常见错误是写成 aria-label="Navigation" 或直接省略——前者太泛,后者让辅助技术默认读作“导航”,无法区分层级关系。
-
aria-label="Breadcrumb"最简明,推荐首选(英文界面) - 中文界面可用
aria-label="面包屑导航",但需确认屏幕阅读器对中文 label 支持稳定(NVDA + Chrome 表现良好,VoiceOver 偶尔略卡顿) - 避免用
role="navigation"单独修饰<ol>或<div>:语义重复且削弱<nav>的结构意义
ol + li 是语义最优解,别用 div 堆砌
面包屑本质是**有顺序的层级路径**,<ol> 天然表达“有序列表”和“位置可数”(如“第1项:首页 → 第2项:产品 → 第3项:Web组件”),屏幕阅读器会自动读出序号,帮助用户定位当前深度。
用 <div> 或 <span> 拼接,即使加了 aria-label,也丢失了顺序语义,键盘用户 Tab 到此处时无法感知“这是第几级”。
立即学习“前端免费学习笔记(深入)”;
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
- 每个
<li>内部:当前页用<span>(不可聚焦),上级页用<a>(带href) - 末尾项(当前页)**不要加
<a>**,也不要用tabindex="-1"伪装链接——这会让屏幕阅读器误判为“可操作但禁用”,引发困惑 - 分隔符(如
/或>)建议用<li aria-hidden="true">包裹,或纯 CSS 生成(::after),避免被读出
当前页需要 aria-current="page",不是 aria-disabled
这是最常被搞错的一点:很多人以为“当前页不可点”就该加 aria-disabled="true"。但 aria-disabled 暗示“本可交互,只是暂时禁用”,而当前页是**语义上无需跳转的状态**,应使用 aria-current="page"。
支持无障碍的屏幕阅读器(JAWS、NVDA、VoiceOver)遇到 aria-current="page" 会明确读作“当前页面:设计系统”,并通常配合音调变化提示用户已到达终点。
- 只加在当前页对应的
<li>或其内部<span>上,不要加在<nav>根节点 - 值必须是
"page"(不能是"true"或"current") - 如果面包屑中某级是“分类页”,但用户实际在该分类下的某篇文章里,那么“分类页”仍是可点击的上级,只有“文章标题”一项加
aria-current="page"
CSS 隐藏视觉分隔符时,aria-hidden 必须同步处理
用 CSS 的 display: none 或 visibility: hidden 隐藏 / 符号,会导致它既不显示也不被读出——这没问题;但若用 opacity: 0 或 position: absolute; left: -9999px,元素仍存在于可访问树中,屏幕阅读器可能把它读成“斜杠”或“空格”,干扰理解。
正确做法是:把分隔符放进一个显式声明 aria-hidden="true" 的容器里,确保它彻底从辅助技术中移除。
- 错误:
<span style="opacity: 0">/</span>→ 可能被读出 - 正确:
<li aria-hidden="true"><span>/</span></li>或<span aria-hidden="true">/</span> - 更干净的做法:完全用伪元素生成分隔符(
ol li:not(:last-child)::after { content: "/"; }),天然不进可访问树
真正容易被忽略的,是面包屑在动态路由(如 React Router、Vue Router)中的更新时机——aria-current 必须随 URL 实时同步,否则屏幕阅读器会卡在旧状态;服务端渲染时也要确保首屏 HTML 就包含正确的 aria-current 属性,不能依赖 JS 注入。


















