前端无障碍(Accessibility,常缩写为 a11y)不是锦上添花,而是网站能不能被所有人使用的底线。据 WHO 统计,全球有超过 10 亿人存在某种形式的障碍;当你的按钮没有可访问名称、图片没有 alt 文本、弹窗无法用键盘关闭时,这群用户会被直接挡在门外。本文从语义化 HTML、ARIA、键盘可达、对比度到自动化检测,给出一套可立即落地的前端无障碍实战清单。
一、先搞清楚:前端无障碍到底在解决什么
无障碍(a11y)指的是让视觉、听觉、运动、认知有障碍的用户,也能平等地感知、理解并操作网页。它通常遵循 W3C 的 WCAG(Web Content Accessibility Guidelines) 标准,分为 perceivable(可感知)、operable(可操作)、understandable(可理解)、robust(健壮)四个原则,简称 POUR。
对技术团队来说,做前端无障碍有三重收益:一是覆盖更大的用户群;二是可访问的语义化结构往往也利于 SEO 与屏幕阅读器之外的爬虫;三是很多国家(如欧盟 EN 301 549、美国 ADA)已将网站无障碍纳入合规要求,遗漏可能带来法律风险。
二、语义化 HTML:成本最低的第一道防线
很多无障碍问题,根本不需要 ARIA,只要用对原生标签就能解决。屏幕阅读器依赖 DOM 的语义角色来播报,而 <div> 和 <span> 默认是“无角色”的,滥用它们构建 UI 会丢掉大量内置可访问性。
2.1 用对标签,而不是用 div 模拟
<!-- 反面教材:用 div 拼按钮,屏幕阅读器读不出“按钮” -->
<div class="btn" onclick="submit()">提交</div>
<!-- 正确:原生 button 自带角色、键盘可达、焦点可见 -->
<button type="submit">提交</button>
<!-- 链接用 a,导航用 nav,正文分区用 main/section/article -->
<nav aria-label="主导航">
<ul><li><a href="/">首页</a></li></ul>
</nav>
<main>
<article>...正文...</article>
</main>
2.2 用 landmark 帮用户快速跳转
header / nav / main / aside / footer 这些 landmark(地标)区域,能让屏幕阅读器用户用快捷键在不同区块间跳转,而不是从头读到底。一个页面应有且仅有一个 main,多个 nav 要用 aria-label 区分。
三、ARIA:当原生语义不够时再出场
ARIA(Accessible Rich Internet Applications)通过 role 和 aria-* 属性补充语义,但它有一条黄金法则:能用原生 HTML 就不要加 ARIA。错误的 ARIA 比没有 ARIA 更糟,因为它会向辅助技术撒谎。
3.1 给自定义组件补上角色与状态
<!-- 自定义 Tab 切换:用 role 声明结构,用 aria-selected 暴露状态 -->
<div role="tablist">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">概览</button>
<button role="tab" id="tab-2" aria-selected="false" aria-controls="panel-2">详情</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">...</div>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>...</div>
3.2 用 aria-live 播报动态内容
表单校验错误、Toast 提示、加载状态这类“动态出现”的内容,屏幕阅读器默认不会主动播报。给容器加 aria-live="polite",辅助技术会在内容变化时朗读出来,不打断当前操作。
<div aria-live="polite" class="toast">
<!-- 当这里文本变化(如“已保存”“验证码错误”),会被朗读 -->
</div>
四、键盘可达与焦点管理
很多用户完全不用鼠标。可操作元素必须能用 Tab 到达、用 Enter/Space 触发,且焦点顺序要符合视觉顺序。最容易被忽视的是模态框:打开时焦点应移入弹窗,关闭后回到触发元素,且 Tab 不能“逃出”弹窗。
4.1 实现一个焦点陷阱(focus trap)
function trapFocus(container) {
const focusable = container.querySelectorAll(
'a[href], button:not([disabled]), input, [tabindex]:not([tabindex="-1"])'
);
const first = focusable[0], last = focusable[focusable.length - 1];
container.addEventListener('keydown', (e) => {
if (e.key !== 'Tab') return;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault(); last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault(); first.focus();
}
});
}
4.2 跳过链接(skip link)
在 body 开头放一个“跳到主内容”的链接,默认隐藏、获得焦点时显示,键盘用户就能一键绕过冗长导航直达正文,这是 WCAG 2.4.1 的明确要求。
五、可访问的表单:每个控件都要有名字
屏幕阅读器读表单时,靠的是“控件 + 关联标签”的组合。没有 <label> 的输入框,用户听到的只是“编辑框”,完全不知道要填什么。错误提示要用 aria-describedby 关联到输入框。
<label for="email">邮箱</label>
<input id="email" type="email" aria-required="true"
aria-describedby="email-err">
<p id="email-err" role="alert">邮箱格式不正确</p>
六、颜色对比度:别让低视力用户看不见
WCAG 对正文文本要求对比度至少 4.5:1(大号文本 3:1)。浅灰字配白底、品牌色做正文是常见雷区。可以用浏览器开发者工具或对比度检查器核对。
| 等级 | 对比度要求 | 适用场景 |
|---|---|---|
| AA(普通文本) | ≥ 4.5:1 | 绝大多数正文、按钮文字 |
| AA(大文本 ≥24px) | ≥ 3:1 | 标题、大字标语 |
| AAA(普通文本) | ≥ 7:1 | 无障碍高标准站点 |
| UI 组件 / 图形 | ≥ 3:1 | 输入框边框、图标按钮 |
另外,不要只用颜色传达信息。表单错误除了标红,还应加图标或文字“错误”,否则色盲用户无法区分。
七、图片与多媒体:给非视觉用户留通道
装饰性图片用 alt="" 让屏幕阅读器跳过;承载信息的图片必须写清楚它“表达什么”。纯文字图片(如带标题的 banner)尤其要避免,因为读屏软件读不到图里的字。
<!-- 装饰图:空 alt,被忽略 -->
<img src="/deco.png" alt="">
<!-- 信息图:描述含义而非“一张图” -->
<img src="/sales.png" alt="2026 年 Q3 销售额环比增长 23%,主要来自华东大区">
八、用工具把无障碍做成可自动检查的项
人工审计成本高,先让工具兜底 30% 的硬错误。Lighthouse 的 Accessibility 审计、axe DevTools 浏览器插件都能在开发期直接标红问题;在 CI 里集成 axe-core 或 eslint-plugin-jsx-a11y,可以把无障碍问题挡在合并之前。顺带一提,Lighthouse 同样覆盖前端性能优化的审计项,一次跑测两件事。在 VS Code 中装一个 axe 插件,写代码时就能实时看到警告。
| 工具 | 类型 | 擅长 |
|---|---|---|
| Lighthouse | 浏览器/CLI | 一键出综合无障碍评分与改进项 |
| axe-core / axe DevTools | 库 / 插件 | 细粒度规则、CI 可集成 |
| eslint-plugin-jsx-a11y | lint 规则 | React/Vue 写码期即报错 |
| WAVE / 读屏实测 | 在线 / 人工 | 终态验收、真实体验验证 |
九、在 Vue / React 框架里落地
框架本身不阻碍无障碍,但 SPA 路由切换、动态内容会让焦点“丢失”。以 Vue 3 为例,路由切换后手动把焦点移到新页面标题,能保证键盘和读屏用户不会停留在旧位置:
// Vue Router 切换后,把焦点交给主区域
router.afterEach(() => {
nextTick(() => {
const main = document.querySelector('main');
if (main) {
main.setAttribute('tabindex', '-1');
main.focus();
}
});
});
在 GitHub Actions 的构建步骤里跑一次 axe-core,能让每次 PR 都自动过一道无障碍门禁,避免后期返工。
十、上线前无障碍检查清单
| 检查项 | 方法 | 对应原则 |
|---|---|---|
| 仅用键盘能否走完主流程 | 拔掉鼠标实操一遍 | Operable |
| 所有图片有合适 alt | axe / 人工核对 | Perceivable |
| 对比度达标 | 对比度检查器 | Perceivable |
| 动态内容有 aria-live | 读屏实测 | Robust |
| 标题层级无跳跃 | 检查 h1→h2→h3 | Understandable |
| CI 跑了 a11y 测试 | 流水线绿 | Robust |
小结
前端无障碍不是一次性改造,而是一种贯穿需求、开发、测试的工程习惯。记住优先级:先语义化、再 ARIA、最后用工具兜底。从今天起,给每个按钮用 <button>、给每张图写 alt、给表单加 label,你的网站就会对更多人真正“可用”。




