HTML无障碍是系统性实践,需严格遵循WCAG 2.1 AA级标准;lang属性缺失会导致语音错读与aria-live失效;标题必须语义化层级递进;表单必须显式关联label;dialog需手动管理焦点流。

HTML无障碍可访问性不是“加个alt就行”,而是从文档结构、语义标签、焦点流到状态管理的系统性实践。WCAG 2.1 AA级是当前国内政务、教育、国企类项目硬性验收线,不达标可能直接导致合同支付中止。
为什么<html lang="zh-CN">不能省略
屏幕阅读器依赖lang属性决定发音规则和语音引擎切换。缺失该属性时,NVDA或VoiceOver会按默认语言(通常是英语)朗读中文,造成大量音节错读或停顿异常;更关键的是,部分辅助技术将lang作为内容理解的前提,缺失会导致表单错误提示、动态更新区域(aria-live)静默失效。
实操建议:
-
lang值必须匹配实际内容语言,如简体中文用zh-CN,繁体中文用zh-TW,混合页面需在局部元素上覆盖,例如<span lang="en">API</span> - 避免写成
lang="zh"或lang="chinese"——前者无区域标识,后者非标准BCP 47码,AXE等扫描工具会报严重级缺陷 - 服务端渲染场景下,确保模板中
<html>标签的lang由真实用户语言偏好注入,而非固定写死
<h1>到<h6>的层级断裂为什么比样式错更危险
视觉用户靠字体大小识别标题级别,但屏幕阅读器用户依赖标题层级构建内容脑图。跳过<h2>直接写<h3>,会让NVDA的“标题导航”模式漏掉整块逻辑单元;而用CSS把<div class="title-h2">强行撑大,对辅助技术完全不可见。
立即学习“前端免费学习笔记(深入)”;
实操建议:
- 标题必须用
<h1>–<h6>原生标签,禁止用<div>+CSS模拟 - 一个页面有且仅有一个
<h1>(通常为页面主标题),后续层级严格递进,不跳跃、不倒置 - 若因设计限制无法按语义排版(如卡片内需要独立小标题),用
role="heading"+aria-level="2"补救,但这是退化方案,优先重构HTML结构
表单控件没配<label>或aria-labelledby会卡住谁
键盘用户Tab到输入框时,若无明确关联标签,屏幕阅读器只会报“编辑文本,空白”,完全不知道该填什么;视障用户反复按Tab却不知当前字段用途,直接放弃操作。AXE扫描中“Form elements must have labels”属于A级必修项,2023年某省级人社厅项目就因此被拦在验收环节。
实操建议:
- 首选显式
<label for="id">+<input id="id">配对,ID必须全页唯一且大小写敏感 - 禁用隐式包裹写法
<label>姓名<input></label>——当<input>是type="hidden"或被JS动态移入移出时,关联极易断裂 - 复杂场景(如多控件共用一个说明文字)用
aria-labelledby="id1 id2",ID列表以空格分隔,顺序即朗读顺序
<dialog>自带可访问性,但为什么仍要手动处理焦点
<dialog>调用showModal()后,浏览器自动聚焦首个可交互元素并锁住外部焦点——这看似完美,但实际中常因初始焦点元素缺失(如对话框内全是<p>)、或JS异步插入内容导致焦点未就位,引发屏幕阅读器卡在空白背景层。
实操建议:
- 每个
<dialog>内部必须有且仅有一个默认可聚焦元素(如<button>或<input>),并在showModal()后立即显式调用.focus() - 监听
close事件,在关闭后把焦点切回触发按钮(document.getElementById('openDialogBtn').focus()),否则键盘用户会迷失在页面顶部 - 避免在
<dialog>里放tabindex="-1"的容器再手动管理焦点陷阱——这会覆盖原生行为,增加出错概率
最易被忽略的点:可访问性不是上线前加个插件扫描就能闭环的事。它从<html lang>那一刻就已开始,贯穿每个<label>绑定、每处aria-* 补全、每次dialog打开与关闭。一次DOM结构调整,可能让整个键盘导航链断裂;一个lang拼写错误,会让整页中文变成乱码语音。它不炫技,但容错率极低。



















