屏幕阅读器无法正确识别导航区域,若无链接或缺失aria-label,则读作“一组链接”而非“主导航”,且无法用n键跳转;必须唯一且不嵌套,否则at无法定位主内容;按钮须用原生标签并设type="button",禁用状态需用disabled属性;标题层级须连续,h1不可缺失或重复,避免语义与视觉脱节。

屏幕阅读器能否正确识别导航区域
如果 <nav></nav> 没有包裹可操作链接,或缺失 aria-label,NVDA、VoiceOver 等工具会把它读作“一组链接”,而非“主导航”。用户无法用快捷键(如 N 键)直接跳转。
常见错误包括:
- 用
<div class="nav"> 替代 <code><nav></nav> -
<nav></nav>内部只有文字、图标,没有<a></a>或<button></button> - 多个
<nav></nav>未区分用途,比如没给侧边栏导航加aria-label="侧边栏导航" - 服务端渲染模板中,
<main></main>被重复注入(如 header 组件和 page 组件各自输出一个) - SPA 应用中,路由切换时未清空旧
<main></main>,导致 DOM 中残留多个 -
<main></main>包裹了页脚或广告位等非核心内容 - 是否用了
<button></button>?——优先选它,无需额外绑定事件 - 是否设置了
type="button"?——避免表单意外提交 - 是否用
<a href="#"></a>代替<button></button>?——这是反模式,href="#" 会触发页面滚动且无语义 - 禁用状态是否同时用了
disabled属性和 CSSpointer-events: none?——后者会阻断 AT 识别 -
<h1></h1>缺失或重复(如每个组件都渲染一个<h1></h1>) - 用
<h2></h2>做副标题但实际是视觉装饰,应改用<span></span>+ CSS 样式 - 动态渲染的标题(如 React 中 useEffect 设置
document.title)未同步更新 DOM 中的<h1></h1> - 多语言站点中,
<h1></h1>文本与<title></title>不一致,导致 AT 用户困惑
修复建议:每个 <nav></nav> 必须含至少一个可聚焦元素,并通过 aria-label 或 aria-labelledby 明确其功能。
main 元素是否唯一且位置合理
<main></main> 是屏幕阅读器“跳过导航”的默认锚点。若页面存在多个 <main></main>,或它被嵌套在 <article></article> / <section></section> 内部,AT(辅助技术)将无法准确定位主内容区。
典型问题场景:
验证方式:在 Chrome DevTools 的 Accessibility 面板中搜索 main,确认只出现一次,且其 role="main" 属性未被覆盖。
按钮与链接是否具备原生语义和键盘支持
用 <div onclick="..."> 或 <code><span tabindex="0"></span> 模拟按钮,即使加了 aria-role="button",仍可能缺失空格/回车触发、焦点管理、禁用状态样式同步等问题。
关键判断点:
真实案例:某电商商品卡片的“加入购物车”用 <div class="btn"> 实现,键盘用户 Tab 到该元素后按空格无响应,必须改用 <code><button type="button"></button> 才能修复。
标题层级是否连续且反映真实内容结构
屏幕阅读器用户常靠 H1–H6 快速扫描页面逻辑。跳过一级标题(如从 <h1></h1> 直接到 <h3></h3>),或用 CSS 隐藏 <h2></h2> 却不移除语义,都会破坏上下文感知。
容易忽略的细节:
检查方法:打开 axe DevTools,运行“Heading levels”规则;或在 VoiceOver 中按 H 键遍历所有标题,听是否连贯、无跳跃。
HTML 代码质量的无障碍维度,不取决于你写了多少 ARIA 属性,而在于你有没有让原生标签各司其职。最常被绕过的其实是 <main></main> 的唯一性、<button></button> 的默认行为、以及标题的实际层级——这些地方一出错,辅助技术就失去上下文,再漂亮的 ARIA 也补不回来。











