关于 accessibility
面向开发者和审查人员的无障碍技能,依据 WCAG 2.2 和 Lighthouse。它结合页面渲染证据、键盘操作与人工检查,不把自动化评分视为合规证明。
主要功能
- 01WCAG 2.2 指南
- 02Lighthouse 与 axe 审查流程
- 03无障碍树与键盘检查
- 04实用实现模式
- 05人工测试清单
适用场景
- 01审查渲染页面的无障碍问题
- 02将审查失败定位到组件或模板
- 03检查键盘导航与焦点行为
MCP 相关
在可用 Chrome DevTools MCP 时,流程使用 `lighthouse_audit` 和 `take_snapshot` 检查页面渲染后的无障碍问题。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ addyosmani/ web-quality-skills --skill accessibility安装后在 Claude Code 中输入/accessibility确认已加载。
附带文件references/A11Y-PATTERNS.mdreferences/WCAG.md
详细介绍
无障碍(a11y)
基于 WCAG 2.2 和 Lighthouse 无障碍审计的全面无障碍指南。目标:让所有人都能使用内容,包括残障人士。
基于证据的审计工作流
当页面已渲染时:
- 如果具备相应能力,运行实时 Lighthouse 无障碍审计;使用 Chrome DevTools MCP 时,调用
lighthouse_audit。对于一般公共页面,使用移动端导航模式;如果重新加载会丢失已验证身份或用户创建的状态,则使用快照模式。 - 根据审计失败的节点定位相关组件或模板,而不是在整个代码仓库中搜索通用模式。
- 检查已渲染的无障碍树快照,查看名称、角色、状态、地标和标题结构;使用 Chrome DevTools MCP 时,调用
take_snapshot。使用键盘操作受影响的流程。 - 修复源代码,然后重新运行相同的审计和手动交互测试。
如果无法使用实时工具,请使用 Lighthouse CLI 或 axe 进行自动化检查,并完成相同的手动检查。自动化工具只能检测部分无障碍障碍:得分为 100 并不代表符合 WCAG,低分也不能替代针对具体问题的证据。
WCAG 原则:POUR
| 原则 | 描述 |
|---|---|
| Perceivable(可感知) | 内容可通过不同感官感知 |
| Operable(可操作) | 所有用户都能操作界面 |
| Understandable(可理解) | 内容和界面易于理解 |
| Robust(健壮) | 内容可与辅助技术配合使用 |
符合等级
| 等级 | 要求 | 目标 |
|---|---|---|
| A | 最低无障碍要求 | 必须通过 |
| AA | 标准合规 | 应通过(许多司法管辖区的法律要求) |
| AAA | 增强无障碍性 | 最好达到 |
可感知
文本替代项(1.1)
图片需要替代文本:
<img src="chart.png">
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">
<img src="decorative-border.png" alt="" role="presentation">
<figure>
<img src="infographic.png" alt="2024 market trends infographic"
aria-describedby="infographic-desc">
<figcaption id="infographic-desc">
</figcaption>
</figure>
图标按钮需要无障碍名称:
<button><svg></svg></button>
<button aria-label="Open menu">
<svg aria-hidden="true"></svg>
</button>
<button>
<svg aria-hidden="true"></svg>
<span class="visually-hidden">Open menu</span>
</button>
视觉隐藏类:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
色彩对比度(1.4.3、1.4.6)
| 文本大小 | AA 最低要求 | AAA 增强要求 |
|---|---|---|
| 普通文本(< 18px / < 14px 粗体) | 4.5:1 | 7:1 |
| 大号文本(≥ 18px / ≥ 14px 粗体) | 3:1 | 4.5:1 |
| UI 组件和图形 | 3:1 | 3:1 |
/* ❌ Low contrast (2.5:1) */
.low-contrast {
color: #999;
background: #fff;
}
/* ✅ Sufficient contrast (7:1) */
.high-contrast {
color: #333;
background: #fff;
}
/* ✅ Focus states need contrast too (3:1 against background, WCAG 1.4.11) */
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
不要仅依赖颜色传达信息:
<input class="error-border">
<style>.error-border { border-color: red; }</style>
<div class="field-error">
<input aria-invalid="true" aria-describedby="email-error">
<span id="email-error" class="error-message">
<svg aria-hidden="true"></svg>
Please enter a valid email address
</span>
</div>
媒体替代项(1.2)
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English" default>
<track kind="descriptions" src="descriptions.vtt" srclang="en" label="Descriptions">
</video>
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
</audio>
<details>
<summary>Transcript</summary>
<p>Full transcript text...</p>
</details>
可操作
键盘可访问性(2.1)
所有功能都必须支持键盘操作。 优先使用原生交互元素——<button>、<a href> 和表单控件会自动处理 Enter/Space 激活、焦点和辅助技术语义。只有在无法使用原生元素时,才手动添加键盘处理逻辑。
<div class="card" onclick="handleAction()">Open</div>
<button type="button" onclick="handleAction()">Open</button>
// ✅ When you MUST use a non-interactive element (e.g. div with role="button"),
// make it focusable AND handle keyboard activation. Do NOT add this to a native
// <button> — Enter/Space already fire click, so you'd double-trigger.
element.setAttribute('role', 'button');
element.setAttribute('tabindex', '0');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
});
不得造成键盘陷阱。 用户必须能够通过 Tab 键进入和离开每个组件。对话框应使用模态框焦点陷阱模式;原生 <dialog> 元素会自动处理此问题。
焦点可见(2.4.7)
/* ❌ Never remove focus outlines */
*:focus { outline: none; }
/* ✅ Use :focus-visible for keyboard-only focus */
:focus {
outline: none;
}
:focus-visible {
outline: 2px solid currentColor; /* inherits text color → already contrast-checked */
outline-offset: 2px;
}
/* ✅ Or pick a brand color and verify ≥3:1 contrast against every background it lands on */
button:focus-visible {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}
焦点不被遮挡(2.4.11)——2.2 新增
元素获得键盘焦点时,不得被作者创建的其他内容(例如粘性页眉、页脚或重叠面板)完全遮挡。AAA 级别(2.4.12)要求聚焦元素的任何部分都不得被遮挡。
/* ✅ Account for sticky headers when scrolling to focused elements */
:target {
scroll-margin-top: 80px;
}
/* ✅ Ensure focused items clear fixed/sticky bars */
:focus {
scroll-margin-top: 80px;
scroll-margin-bottom: 60px;
}
跳过链接(2.4.1)
提供跳过链接,让键盘用户能够跳过重复导航。完整标记和样式请参阅跳过链接模式。
目标大小(2.5.8)——2.2 新增
交互目标必须至少为 24 × 24 CSS 像素(AA)。例外情况:行内文本链接、大小由浏览器控制的元素,以及以边界框中心为圆心的 24px 圆形不会与其他目标重叠的目标。
/* ✅ Minimum target size */
button,
[role="button"],
input[type="checkbox"] + label,
input[type="radio"] + label {
min-width: 24px;
min-height: 24px;
}
/* ✅ Comfortable target size (recommended 44×44) */
.touch-target {
min-width: 44px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
}
拖动操作(2.5.7)—— 2.2 新增
任何需要拖动的操作都必须提供单指针替代方式(例如按钮、输入框)。可参阅拖动操作模式,了解可排序列表示例。
时间限制(2.2)
// Allow users to extend time limits
function showSessionWarning() {
const modal = createModal({
title: 'Session Expiring',
content: 'Your session will expire in 2 minutes.',
actions: [
{ label: 'Extend session', action: extendSession },
{ label: 'Log out', action: logout }
],
timeout: 120000
});
}
动效(2.3)
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
易于理解
页面语言(3.1.1)
<html>
<html lang="en">
<p>The French word for hello is <span lang="fr">bonjour</span>.</p>
导航一致性(3.2.3)
<nav aria-label="Main">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
帮助一致性(3.2.6)—— 2.2 新增
如果帮助机制(联系信息、聊天组件、FAQ 链接、自助选项)在多个页面重复出现,则每次都必须按相同的相对顺序显示。依赖位置一致性的用户不应每次都要在页面上寻找帮助。
表单标签(3.3.2)
每个输入框都需要关联一个程序化标签。可参阅表单标签模式,了解显式、隐式和说明性示例。
错误处理(3.3.1、3.3.3)
使用 role="alert" 或 aria-live 向屏幕阅读器播报错误,在无效字段上设置 aria-invalid="true",并在提交时将焦点移至第一个错误处。完整标记和 JS 示例请参阅错误处理模式。
重复输入(3.3.7)—— 2.2 新增
不要强迫用户重新输入他们在同一会话中已提供的信息。应从前面的步骤自动填入,或允许用户从先前输入的值中选择。例外情况:安全性重新确认,以及已过期的内容。
<fieldset>
<legend>Shipping address</legend>
<label>
<input type="checkbox" id="same-as-billing" checked>
Same as billing address
</label>
</fieldset>
无障碍身份验证(3.3.8)—— 2.2 新增
登录流程不得依赖认知功能测试(例如记住密码、解谜),除非至少满足以下一项:
- 提供复制粘贴或自动填充机制
- 提供替代方法(例如通行密钥、SSO、电子邮件链接)
- 测试使用物体识别或个人内容(仅适用于 AA;AAA 不包含此例外)
<input type="password" id="password" autocomplete="current-password">
<button type="button">Sign in with passkey</button>
<button type="button">Email me a login link</button>
稳健性
ARIA 使用(4.1.2)
优先使用原生元素:
<div role="button" tabindex="0">Click me</div>
<button>Click me</button>
<div role="checkbox" aria-checked="false">Option</div>
<label><input type="checkbox"> Option</label>
**需要使用 ARIA 时,**应使用正确的角色和状态。完整的选项卡列表示例请参阅ARIA 选项卡模式。
实时区域(4.1.3)
使用 aria-live 区域播报动态内容变化,而不移动焦点。标记和 showNotification() 辅助函数请参阅实时区域模式。
测试清单
自动化测试
优先使用实时 Lighthouse 审计,直接向 Agent 返回未通过审计的已渲染节点。使用 Chrome DevTools MCP 时,该功能为 lighthouse_audit。否则:
# Lighthouse accessibility audit
npx lighthouse https://example.com --only-categories=accessibility
# axe-core
npm install @axe-core/cli -g
axe https://example.com
手动测试
- 键盘导航: 使用 Tab 遍历整个页面,使用 Enter/Space 激活元素
- 屏幕阅读器: 使用 VoiceOver(Mac)、NVDA(Windows)或 TalkBack(Android)进行测试
- 缩放: 内容在 200% 缩放时仍可使用
- 高对比度: 使用 Windows 高对比度模式进行测试
- 减少动效: 使用
prefers-reduced-motion: reduce进行测试 - 焦点顺序: 符合逻辑并遵循视觉顺序
- 目标尺寸: 交互元素至少达到 24×24px
VoiceOver 和 NVDA 快捷键请参阅屏幕阅读器命令参考。
按影响程度划分的常见问题
严重(立即修复)
- 缺少表单标签
- 缺少图像替代文本
- 颜色对比度不足
- 键盘陷阱
- 缺少焦点指示器
较严重(上线前修复)
- 缺少页面语言
- 缺少标题结构
- 链接文本缺乏描述性
- 媒体自动播放
- 缺少跳过链接
中等(尽快修复)
- 图标缺少 ARIA 标签
- 导航不一致
- 缺少错误识别
- 时间限制没有控制选项
- 缺少地标区域