必须唯一且只包核心内容:main 元素须全页唯一、仅包裹用户真正需操作/阅读的内容(如正文、表单主体),否则屏幕阅读器跳转错误;h1 全页唯一且位于 header 内,标题层级严禁跳级;hidden 属性语义屏蔽三重可见性,alt 和 label 必须语义化绑定。

为什么必须唯一且只包核心内容
屏幕阅读器把 main 当作跳转锚点,不是视觉容器。按 M 键时,它只找第一个合法的 main;如果页面里有多个,或它包裹了 banner、侧边栏、广告位,辅助工具要么跳过、要么定位错误——用户得手动听完整页才能找到正文。
常见误用:Next.js 布局组件里写一个 main,子页面又渲染一个;main 里塞了顶部导航+左侧菜单+内容区。
- 正确做法:全 DOM 中仅一个
main,且只包含用户真正要操作/阅读的部分(如商品列表、表单主体、文章正文) - 性能影响:冗余
main不增加字节大小,但会让辅助技术多做无效 DOM 遍历,拖慢语音导航响应 - SSR 场景下尤其危险:Hydration 不同步时,临时出现多个
main,读屏软件可能锁定错误节点
标题层级断裂会让屏幕阅读器彻底迷失上下文
很多开发者用 h2 开头,中间穿插 h1,再回到 h3——这种跳跃会让屏幕阅读器把结构理解成“子章节突然变成父章节”,用户无法建立稳定的认知地图。
关键约束:h1 应全页唯一且位于 header 内;每个 section 或 article 必须以 h2 起始(如果整体页面已用 h1),内部子模块用 h3,依此类推。
立即学习“前端免费学习笔记(深入)”;
- 跳级不是 bug,是可访问性阻断点:浏览器和读屏软件依赖层级生成文档大纲(outline),断裂即丢失逻辑路径
-
h1禁止出现在header外,除非它是页面唯一主标题 - 动态渲染标题(如 React 中通过 props 注入)必须确保 DOM 插入时层级连续,否则首屏读取即错乱
用 hidden 替代 aria-hidden="true" 控制内容可见性
hidden 是 HTML5 原生布尔属性,仅存在即生效:<div hidden>暂不可见的内容</div>。它不是样式技巧,而是语义声明:“这个内容当前不相关、不该被用户感知”。
与 aria-hidden="true" 的本质区别在于:aria-hidden 只影响无障碍树,视觉上仍可见;而 hidden 是视觉 + 可访问性 + 交互能力三重屏蔽——元素从渲染树中移除,同时默认从无障碍树中剔除,即使意外获得焦点也会被浏览器阻止。
- 错误写法:
hidden="true"或hidden=""(虽部分浏览器兼容,但不符合规范) - 服务端渲染时直接输出
hidden即可,如:<p hidden class="error">请输入有效邮箱</p> - 建议在基础 CSS 中加兜底规则:
[hidden] { display: none !important; },防止第三方样式强制覆盖导致内容闪现
图片与表单的语义缺失会触发重复 DOM 操作
缺失 alt 的 <img> 或未绑定 <label> 的 <input>,表面看只是可访问性问题,实则常迫使开发者后期用 JS 补救——比如监听焦点事件手动播报提示、动态插入 ARIA 属性,这些额外逻辑会增加运行时开销和维护风险。
- 图片必须用
<figure>包裹,alt不可为空(装饰图除外),<figcaption>必须存在且与<img>同级 - 表单控件必须显式绑定:
<label for="id">或嵌套写法:<label>邮箱<input type="email"></label> - 常见坑:用 CSS 视觉对齐代替语义绑定;把
title当aria-label用(title不被所有读屏支持)
最容易被忽略的是标题层级断裂和 main 嵌套——它们不会报错,但会让辅助技术在底层逻辑上“认错人”,修复成本远高于初期写对标签。



















