nav必须包裹ul且为顶层地标,不可嵌套于div或section中;多个nav需用aria-label区分;移动端禁用display:none隐藏菜单,须同步控制aria-hidden、inert和aria-expanded。

nav 必须包裹 ul,且不能嵌套在其他地标中
很多开发者用 div 包菜单,或把 nav 塞进 header 里再套一层 section,这会破坏辅助技术对导航区的识别逻辑。浏览器和读屏器(如 NVDA、VoiceOver)依赖 nav 的独立语义定位“这是可跳转的主路径”,一旦被包裹,Ctrl+Alt+O(跳转到主要内容)等快捷键就失效。
正确结构只有这一种:nav → ul → li → a。不要用 div 替代 ul/li,否则键盘 Tab 无法自然遍历,屏幕阅读器也无法播报“列表共 X 项”这类上下文信息。
-
nav必须是顶层地标,同级于main、aside、footer - 多个独立导航区(如顶部 + 侧边栏)要各自用一个
nav,不可合并 -
nav内部禁止出现main、article等内容型语义标签
aria-label 不是可选项,而是必填字段
只写 <nav></nav> 而不加 aria-label,等于告诉屏幕阅读器:“这是一个导航区,但我不知道它叫什么”。当页面含多个 nav(比如顶部主导航、页脚链接、面包屑),用户听到的全是“navigation, navigation, navigation…”——完全无法区分。
必须为每个 nav 显式标注用途:
- 主导航:
<nav aria-label="主导航"></nav> - 文档目录:
<nav aria-label="章节导航"></nav> - 页脚快捷入口:
<nav aria-label="友情链接"></nav> - 面包屑:
<nav aria-label="当前位置"></nav>
避免使用 aria-labelledby 指向隐藏标题(如 display: none 的 h2),读屏器可能跳过;也不要用 aria-label="点击展开菜单" 这类描述交互行为的文案——它该说明“这是什么”,不是“怎么操作”。
aria-current="page" 必须手动同步更新
静态页面中,:target 伪类或服务端渲染能初始化一次 aria-current="page",但 JS 切换路由后,这个属性不会自动更新。若不手动设置,屏幕阅读器永远不知道“你现在在哪一页”,键盘用户也缺乏视觉焦点锚点。
将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。
切换选中项时,必须两步操作:
- 给当前激活的
a元素设置:el.setAttribute('aria-current', 'page') - 清除上一个激活项的该属性:
prevEl.removeAttribute('aria-current')
注意:aria-current 的合法值只有 "page"、"section"、"location" 等有限几个,不能写 "active" 或 "true",否则校验失败。
移动端收起菜单不能用 display: none
用 display: none 隐藏侧边栏或下拉菜单,会导致键盘用户 Tab 键直接跳过整个区域,屏幕阅读器也完全无法感知其存在——这不是“隐藏”,是“删除 DOM 语义”。
正确做法是保留 DOM 结构,仅做视觉隐藏 + 语义控制:
- CSS 层面:用
clip-path: inset(100%)或clip: rect(1px, 1px, 1px, 1px)替代display: none - HTML 层面:同步设置
aria-hidden="true"和inert属性(现代浏览器已支持) - JS 层面:展开时移除
inert和aria-hidden,并确保对应按钮的aria-expanded值为"true"
aria-expanded 必须是字符串 "true" 或 "false",不能传 JavaScript 布尔值;若状态变量为 null 或 undefined,渲染出 aria-expanded="undefined" 就违反 ARIA 规范。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










