lang属性必须写在<html>标签上且值为zh-CN、zh-TW或zh-HK,否则屏幕阅读器会乱读;<main>须为<body>直接子元素且唯一;标题层级不可断裂,否则导航失效。

lang 属性写错或缺失会导致屏幕阅读器乱读
不写 lang,或写成 lang="ch"、lang="cn"、lang="zh",NVDA 和 VoiceOver 就无法准确识别中文。实际表现是:把“登录”读成“トウロク”(日语),或用英语语调硬读汉字,分词错乱,用户根本听不懂。
正确写法只有三种主流值:lang="zh-CN"(简体)、lang="zh-TW"(繁体)、lang="zh-HK"(港澳)。必须写在 <html> 标签上,不能只靠 <meta> 补救;也不能只给局部文字加,比如一段英文引文得单独包 <p lang="en">。
React/Vue 动态渲染时,别用 JS 拼接 lang 值——SSR 或首屏 HTML 必须带对,否则 AT(辅助技术)启动时已错过解析时机。
main 标签嵌套或重复会直接跳过主内容
<main> 是页面唯一主内容区,必须是 <body> 的**直接子元素**。如果包在 <div>、<section> 甚至另一个 <main> 里,NVDA 等工具会忽略它,或误判为普通区块——用户按 M 键想跳主内容,结果什么都没发生。
立即学习“前端免费学习笔记(深入)”;
校验工具(如 axe)会直接报严重错误。常见踩坑点:
- UI 组件库默认把卡片内容塞进
<main>,而父级已有<main>,造成嵌套 - 用 CSS 把
<main>包进布局容器(如<div class="container">),视觉没问题,AT 完全失效 - 多页应用中,JS 动态插入新
<main>但没清理旧的,导致重复
标题层级断裂会让屏幕阅读器导航逻辑崩塌
用户靠 H 键(H1/H2/H3…)逐级跳转内容。一旦出现 <h2> 后直接 <h4>,中间缺了 <h3>,他们就会“掉层”——以为章节结束,实际正文还在继续。这不是提示问题,是结构塌方。
真实原因往往不是手误,而是:
- CSS 隐藏了某个
<h3>(display: none),但 DOM 里还留着,AT 仍按层级解析 - 组件库卡片标题默认用
<h2>,嵌套在<h2>主区域下,变成 H2→H2,跳级了 - JS 动态插入标题时没检查当前上下文层级,直接写
<h4>
修复方式不是加 ARIA,而是删 DOM 或改标签:隐藏标题该用 aria-hidden="true" + 视觉隐藏类,而非 display: none。
button 和 nav 这类语义标签不能靠 class 或 role 模拟
用 <div class="btn"> + JS 绑定 click,再加 role="button" 和 tabindex="0",看似能用,但漏掉关键能力:空格键激活、禁用状态自动灰化、焦点管理、屏幕阅读器播报“按钮”而非“div”。
同理,<nav> 不是“所有链接集合”,而是有明确导航意图的链接组。主导航、面包屑、页内锚点列表可以放进去;文章末尾的“相关阅读”或登录链接不该塞进 <nav>——AT 用户按 N 键快速跳转时,会被拖进一堆无关项。
多个导航区必须区分:<nav aria-label="主导航">、<nav aria-label="页内目录">;面包屑要用 <ol>,当前页加 aria-current="page"。
<html lang="zh-CN"> 这一行有没有写对、<main> 是不是孤零零挂在 <body> 下、<h3> 是不是被 CSS 隐藏却留在 DOM 里——这些地方一错,无障碍就从根上断了。



















