name: 网络可访问性审计 description: 审计网络应用程序以符合WCAG可访问性标准。当被要求运行可访问性检查、识别常见违规并提供修复指导时使用。 tags: [可访问性, a11y, wcag, 测试, 审计] license: MIT
可访问性审计员
通过识别常见违规并提供可操作的修复步骤,审计网络应用程序以符合WCAG 2.0/2.1/2.2标准。
何时使用
- 用户请求可访问性审计、a11y检查或WCAG合规性审查时
- 用户提及可访问性问题、屏幕阅读器或键盘导航问题时
- 用户要求检查或改进针对残疾人士的可访问性时
WCAG原则:POUR
| 原则 | 描述 |
|---|---|
| Perceivable(可感知) | 内容可以通过不同感官感知 |
| Operable(可操作) | 界面可以被所有用户操作 |
| Understandable(可理解) | 内容和界面可理解 |
| Robust(健壮) | 内容与辅助技术兼容 |
合规级别
| 级别 | 要求 | 目标 |
|---|---|---|
| A | 最低可访问性 | 必须通过 |
| AA | 标准合规性 | 应该通过(在许多司法管辖区是法律要求) |
| AAA | 增强可访问性 | 建议拥有 |
12个最常见的WCAG违规
基于WebAIM百万(2021年)研究分析前100万网站:
-
低颜色对比度(WCAG 1.4.3) - 86.4%的网站
- 文本对比度<4.5:1
- 大文本<3:1
- UI组件<3:1
-
缺失/不充分的替代文本(WCAG 1.1.1) - 60.6%的网站
- 图像无alt属性
- 替代文本包含“图像”、“图片”、“照片”
- 有意义的图像使用空alt
-
缺失名称、角色或值(WCAG 4.1.2)
- 交互元素无可访问名称
- 自定义组件无适当ARIA
- 按钮、表单字段、自定义小部件
-
键盘导航失败(WCAG 2.1.1)
- 有onClick但不可键盘访问的元素
- 缺失焦点指示器
- 键盘焦点被锁定
-
未标记的表单控件(WCAG 1.3.1, 3.3.2) - 39.6%的网站
- 输入无<label>或aria-label
- 标签未程序化关联
-
缺失语言属性(WCAG 3.1.1) - 28.9%的网站
- <html>无lang属性
- 缺失外语段落的lang
-
不当的标题结构(WCAG 1.3.1, 2.4.6)
- 跳过的标题级别(h1→h3)
- 多个h1或无h1
- 空标题
-
空链接或差链接文本(WCAG 2.4.4)
- 链接使用“点击这里”、“这里”、“阅读更多”
- 空链接或仅图标链接
-
缺失/不当焦点指示器(WCAG 2.4.7)
- CSS移除轮廓无替代
- 焦点指示器对比度不足
-
过度/误用ARIA(WCAG 4.1.2)
- 当原生HTML有效时使用不必要的ARIA
- 角色无效的ARIA属性
- 缺失必需的ARIA属性
-
不充分的数据表标记(WCAG 1.3.1)
- 表格无<th>元素
- 缺失scope或headers属性
-
缺失媒体字幕(WCAG 1.2.1, 1.2.2)
- 视频无字幕/副标题
- 音频无转录
审计流程
阶段1:自动化测试
运行ESLint(React/JSX项目):
npx eslint --ext .jsx,.tsx --no-ignore --format json . > .claude/skills/a11y-auditor/eslint-results.json 2>&1 || true
或使用帮助脚本:.claude/skills/a11y-auditor/scripts/run-eslint.sh
运行Lighthouse(生产/暂存环境):
npx lighthouse https://example.com --only-categories=accessibility --output=json --output-path=./lighthouse-results.json
检查axe-core集成:
grep -r "@axe-core\|axe-core" package.json
阶段2:手动代码检查
使用references/grep-patterns.md中的grep模式搜索:
- 缺失替代文本
- 键盘导航问题
- 颜色值用于对比检查
- ARIA问题
- 表单标签
- 标题结构
- 语言属性
- 差链接文本
- 媒体元素
查看references/grep-patterns.md获取完整模式列表。
阶段3:分析与优先级排序
使用WCAG影响级别分组发现:
关键(立即修复):
- 键盘锁定
- 无焦点指示器
- 缺失表单标签
- 功能图像缺失替代文本
- 交互元素颜色对比度不足
严重(发布前修复):
- 缺失页面语言
- 不当标题结构
- 非描述性链接文本
- 缺失跳过链接
- 自动播放媒体
中等(尽快修复):
- 图标缺失ARIA标签
- 导航不一致
- 缺失错误标识
- 缺失地标区域
阶段4:手动测试
遵循references/screen-reader-guide.md进行:
- 键盘导航测试
- 屏幕阅读器测试(VoiceOver、NVDA、JAWS)
- 缩放和重排测试
- 高对比度模式测试
- 减少运动测试
WCAG模式示例
可感知
替代文本(1.1.1)
<!-- ❌ 缺失替代文本 -->
<img src="chart.png">
<!-- ✅ 描述性替代文本 -->
<img src="chart.png" alt="柱状图显示第三季度销售额增长40%">
<!-- ✅ 装饰性(空替代文本) -->
<img src="decorative-border.png" alt="" role="presentation">
颜色对比度(1.4.3)
/* ❌ 低对比度(2.5:1) */
.low-contrast {
color: #999;
background: #fff;
}
/* ✅ 足够对比度(7:1) */
.high-contrast {
color: #333;
background: #fff;
}
对比度要求:
- 普通文本:4.5:1(AA)、7:1(AAA)
- 大文本(18px+或14px+粗体):3:1(AA)、4.5:1(AAA)
- UI组件:3:1
媒体替代(1.2)
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English" default>
</video>
可操作
键盘导航(2.1.1)
// ❌ 仅点击
element.addEventListener('click', handleAction);
// ✅ 点击+键盘
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
});
焦点可见(2.4.7)
/* ❌ 永远不移除焦点 */
*:focus { outline: none; }
/* ✅ 仅键盘焦点 */
:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
跳过链接(2.4.1)
<body>
<a href="#main-content" class="skip-link">跳到主要内容</a>
<header><!-- 导航 --></header>
<main id="main-content" tabindex="-1">
<!-- 内容 -->
</main>
</body>
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
减少运动(2.3.3)
@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>法语单词hello是<span lang="fr">bonjour</span>。</p>
表单标签(3.3.2)
<!-- ❌ 无标签 -->
<input type="email" placeholder="Email">
<!-- ✅ 显式标签 -->
<label for="email">电子邮件地址</label>
<input type="email" id="email" autocomplete="email">
<!-- ✅ 带提示 -->
<label for="password">密码</label>
<input type="password" id="password" aria-describedby="password-requirements">
<p id="password-requirements">
必须至少8个字符,包含一个数字。
</p>
错误处理(3.3.1)
<label for="email">电子邮件</label>
<input type="email" id="email"
aria-invalid="true"
aria-describedby="email-error">
<p id="email-error" role="alert">
请输入有效的电子邮件地址。
</p>
健壮
ARIA使用(4.1.2)
<!-- ❌ 不必要的ARIA -->
<button role="button">提交</button>
<!-- ✅ 原生HTML -->
<button>提交</button>
<!-- ✅ 需要时使用ARIA(自定义选项卡) -->
<div role="tablist" aria-label="产品信息">
<button role="tab" aria-selected="true" aria-controls="panel-1">
描述
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2" tabindex="-1">
评论
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
<!-- 内容 -->
</div>
实时区域(4.1.3)
<!-- 礼貌(等待暂停) -->
<div aria-live="polite" aria-atomic="true">
状态更新
</div>
<!-- 断言(中断) -->
<div role="alert" aria-live="assertive">
错误:表单提交失败
</div>
视觉隐藏文本
.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;
}
<button>
<svg aria-hidden="true"><!-- 图标 --></svg>
<span class="visually-hidden">删除项目</span>
</button>
输出格式
生成结构化报告:
# 可访问性审计报告
## 摘要
- 总问题数:X
- 关键:X | 严重:X | 中等:X | 次要:X
- WCAG级别:A、AA或AAA
- 自动化覆盖率:约57%(需要手动测试)
## 关键问题(立即修复)
### 1. [问题名称] - WCAG X.X.X
**严重性:** 关键
**影响:** [谁受影响及如何]
**受影响:** X个元素
**位置:**
- `路径/到/文件.tsx:123`
- `路径/到/文件.tsx:456`
**问题:**
[简要描述]
**修复:**
```tsx
// 之前
<div onClick={handleClick}>点击我</div>
// 之后
<button onClick={handleClick}>点击我</button>
原因: [可访问性原则]
严重问题
[相同格式]
中等问题
[相同格式]
测试建议
- 手动键盘测试(Tab、Enter、Escape)
- 屏幕阅读器测试(见references/screen-reader-guide.md)
- 自动化测试设置(@axe-core/react或Lighthouse CI)
- 颜色对比度验证(WebAIM对比度检查器)
后续步骤
[优先行动项]
---
## 工具与资源
### 开发工具
- **eslint-plugin-jsx-a11y** - React/JSX静态分析(约37条规则)
- **axe-core DevTools** - 浏览器扩展用于运行时测试
- **Lighthouse** - Chrome DevTools内置
### 测试工具
- **@axe-core/react** - 运行时可访问性测试
- **@axe-core/playwright** - E2E测试集成
- **pa11y** - 自动化命令行测试
### 手动测试
- **WebAIM对比度检查器** - https://webaim.org/resources/contrastchecker/
- **WAVE** - 浏览器扩展用于视觉反馈
- **屏幕阅读器** - NVDA(Windows)、VoiceOver(macOS)、JAWS
### 参考文档
- `references/WCAG-criteria.md` - 所有WCAG 2.1成功标准
- `references/ARIA-patterns.md` - 常见ARIA模式和示例
- `references/screen-reader-guide.md` - 测试命令和场景
- `references/grep-patterns.md` - 代码审计搜索模式
### 参考
- [WebAIM百万](https://webaim.org/projects/million/) - 前100万网站年度分析(违规统计)
- [WCAG 2.1快速参考](https://www.w3.org/WAI/WCAG21/quickref/) - 交互式WCAG指南
- [WAI-ARIA编写实践](https://www.w3.org/WAI/ARIA/apg/) - 官方ARIA模式
- [Deque axe规则](https://dequeuniversity.com/rules/axe/) - 所有axe-core规则解释
- [jsx-a11y规则](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y#supported-rules) - ESLint可访问性规则
---
## 重要注意事项
- 自动化工具捕获30-57%的问题;需要手动测试
- 使用ARIA的页面比不使用平均多41%的错误
- 尽可能使用实际辅助技术测试
- 首先关注关键问题(键盘、屏幕阅读器、对比度)
- 记录深思熟虑的可访问性决策
- 在多个浏览器和设备上测试
- 可能时包括残疾用户测试
## 常见陷阱避免
1. 仅依赖自动化测试
2. 当原生HTML足够时使用ARIA
3. 移除焦点指示器
4. 使用正tabindex值
5. 仅用颜色传达信息
6. 模态/对话框中键盘锁定
7. 非描述性链接文本
8. 缺失或不正确的标题层次结构
9. 未标记的表单控件
10. 缺失语言属性