html语义结构必须对齐读屏逻辑,因屏幕阅读器解析的是可访问性树而非源码;须为直接子元素且唯一,标题层级不可断裂,lang必须为zh-cn等标准值,否则导致导航失效或乱码。

HTML 语义结构没对齐读屏逻辑,再细的样式优化也白搭。 屏幕阅读器不是“读 HTML 源码”,而是解析浏览器生成的可访问性树(Accessibility Tree);校准代码质量,本质是让 DOM 结构、标签语义、ARIA 属性三者协同输出准确的 Role / Name / States。
为什么 Chrome 的 Accessibility Inspector 是唯一可信起点
它不看你写了什么 ARIA,只显示辅助技术最终“看到”的计算结果。很多团队在本地用 NVDA 测试没问题,上线后用户反馈“跳不过导航”——往往是因为没开这个面板验证真实输出。
-
Computed Role显示generic?优先检查是否漏了<label for="id"></label>或用了<div> 冒充按钮/表单控件 <li> <code>Name列为空?说明屏幕阅读器根本没抓到可读文本,aria-label和<label></label>混用时,ChromeVox 会取前者,NVDA 可能忽略后者 -
States不同步?比如 JS 改了input.checked = true,但没触发change事件,读屏器仍报“未选中” - 禁止写成:
<div class="wrapper"><main>...</main></div> - 禁止重复:
<main>首页</main>...<main>文章正文</main>—— axe 工具报critical错误 - JS 动态替换
<main></main>内容时,必须加aria-live="polite",否则更新不播报 - 常见诱因:
display: none隐藏了<h3></h3>却没删 DOM;组件库默认卡片标题用<h2></h2>,嵌在<h2></h2>主区下导致 H2→H2 - 修复原则:每个
<section></section>必须以<h2></h2>或更高级别标题开头;局部重置层级需显式声明,不能靠 CSS 隐藏“绕过” - 不要用
aria-hidden="true"+ 视觉隐藏类替代删 DOM —— 它会让标题从可访问性树消失,但仍在 DOM 中干扰层级 - 正确值只有:
lang="zh-CN"(简体)、lang="zh-TW"(繁体)、lang="zh-HK"(港澳) - 必须写在
标签上,<meta>或局部lang补丁无效 - React/Vue 中 SSR 渲染时,
lang值必须随服务端 locale 动态注入,JS 拼接易出错,建议走模板直出
<main></main> 必须是 直接子元素且仅出现一次
这不是规范洁癖,而是 WAI-ARIA 定义的硬约束:<main></main> 是唯一被 AT(辅助技术)识别为主内容入口的地标元素。嵌套在 <div> 或 <code><section></section> 里,NVDA 2025+ 会直接跳过整块区域。
标题层级断裂比样式错位更致命
屏幕阅读器用户按 H 键逐级跳转,<h2></h2> 后直接 <h4></h4> 不是“少个标题”,是结构塌方:他们以为章节已结束,实际内容还在继续。
lang 属性写错等于主动拒绝对话
lang="zh" 或 lang="ch" 看似省事,实则让 VoiceOver/NVDA 把简体中文当日文朗读,分词错乱、语调崩坏。这不是“读得不准”,是根本听不懂。
最常被忽略的点:语义校准不是一次性动作。每次 DOM 动态插入(尤其是第三方 SDK 注入的广告、埋点脚本),都可能污染可访问性树——必须用 Accessibility Inspector 复测关键路径,而不是只信初始 HTML。











