关于 wcag-audit-patterns
一项依据 WCAG 2.2 审查网页内容并规划整改的参考技能,面向开发者和无障碍审查人员,提供检查清单、代码示例及自动化测试指引。
主要功能
- 01WCAG 符合等级与 POUR 原则
- 02覆盖无障碍标准的审查清单
- 03常见问题及整改模式
- 04axe-core 与 Playwright 测试示例
- 05无障碍测试命令行示例
适用场景
- 01检查网站的 WCAG 问题
- 02规划表单标签和键盘访问整改
- 03在 Playwright 测试中加入无障碍检查
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill wcag-audit-patterns安装后在 Claude Code 中输入/wcag-audit-patterns确认已加载。
附带文件agents/openai.yaml
详细介绍
WCAG 审计模式
全面介绍如何依据 WCAG 2.2 指南审计网页内容,并提供可执行的修复策略。
何时使用此技能
- 开展无障碍审计
- 修复 WCAG 违规问题
- 实现无障碍组件
- 为无障碍诉讼做准备
- 满足 ADA/Section 508 要求
- 达到 VPAT 合规要求
核心概念
1. WCAG 合规等级
| 等级 | 描述 | 适用要求 |
|---|---|---|
| A | 最低无障碍要求 | 法律基线 |
| AA | 标准合规 | 大多数法规 |
| AAA | 增强型无障碍要求 | 特殊需求 |
2. POUR 原则
Perceivable: Can users perceive the content?
Operable: Can users operate the interface?
Understandable: Can users understand the content?
Robust: Does it work with assistive tech?
3. 按影响程度划分的常见违规问题
Critical (Blockers):
├── Missing alt text for functional images
├── No keyboard access to interactive elements
├── Missing form labels
└── Auto-playing media without controls
Serious:
├── Insufficient color contrast
├── Missing skip links
├── Inaccessible custom widgets
└── Missing page titles
Moderate:
├── Missing language attribute
├── Unclear link text
├── Missing landmarks
└── Improper heading hierarchy
审计清单
可感知性(原则 1)
## 1.1 Text Alternatives
### 1.1.1 Non-text Content (Level A)
- [ ] All images have alt text
- [ ] Decorative images have alt=""
- [ ] Complex images have long descriptions
- [ ] Icons with meaning have accessible names
- [ ] CAPTCHAs have alternatives
Check:
```html
<img src="chart.png" alt="Sales increased 25% from Q1 to Q2" />
<img src="decorative-line.png" alt="" />
<img src="chart.png" />
<img src="decorative-line.png" alt="decorative line" />
```
1.2 基于时间的媒体
1.2.1 纯音频和纯视频(A级)
- 音频提供文本转录稿
- 视频提供音频描述或转录稿
1.2.2 字幕(A级)
- 所有视频都提供同步字幕
- 字幕准确且完整
- 包含说话人标识
1.2.3 音频描述(A级)
- 视频为视觉内容提供音频描述
1.3 适应性
1.3.1 信息和关系(A级)
- 标题使用正确的标签(h1-h6)
- 列表使用 ul/ol/dl
- 表格包含表头
- 表单输入项带有标签
- 存在 ARIA 地标
检查:
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another Section</h2>
<table>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Price</th>
</tr>
</thead>
</table>
1.3.2 有意义的顺序(A级)
- 阅读顺序符合逻辑
- CSS 定位不会打乱顺序
- 焦点顺序与视觉顺序一致
1.3.3 感官特征(A级)
- 说明不单独依赖形状或颜色
- “点击红色按钮” → “点击提交(红色按钮)”
1.4 可辨识性
1.4.1 颜色的使用(A级)
- 不单独依靠颜色传达信息
- 链接无需借助颜色也能辨识
- 错误状态不只通过颜色表示
1.4.3 对比度(最低)(AA级)
- 文本:对比度至少为 4.5:1
- 大号文本(18pt 以上):对比度至少为 3:1
- UI 组件:对比度至少为 3:1
工具:WebAIM Contrast Checker、axe DevTools
1.4.4 调整文本大小(AA级)
- 文本可放大至 200%,且内容不丢失
- 在 320px 宽度下无需水平滚动
- 内容可正常重排
1.4.10 重排(AA级)
- 内容在 400% 缩放下可重排
- 无需二维滚动
- 在 320px 宽度下可访问所有内容
1.4.11 非文本对比度(AA级)
- UI 组件的对比度至少为 3:1
- 焦点指示器清晰可见
- 图形对象易于辨识
1.4.12 文本间距(AA级)
- 增大间距后内容不会丢失
- 行高为字体大小的 1.5 倍
- 段落间距为字体大小的 2 倍
- 字符间距为字体大小的 0.12 倍
- 单词间距为字体大小的 0.16 倍
### Operable (Principle 2)
```markdown
## 2.1 Keyboard Accessible
### 2.1.1 Keyboard (Level A)
- [ ] All functionality keyboard accessible
- [ ] No keyboard traps
- [ ] Tab order is logical
- [ ] Custom widgets are keyboard operable
Check:
```javascript
// Custom button must be keyboard accessible
<div role="button" tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') activate()">
2.1.2 无键盘陷阱(A级)
- 焦点可移出所有组件
- 模态对话框可正确锁定焦点
- 模态对话框关闭后焦点返回
2.2 时间充足
2.2.1 可调整计时(A级)
- 可延长会话超时时间
- 超时前向用户发出警告
- 可禁用自动刷新
2.2.2 暂停、停止、隐藏(A级)
- 可暂停移动内容
- 可暂停自动更新的内容
- 动画遵循 prefers-reduced-motion 设置
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}
2.3 癫痫发作和生理反应
2.3.1 三次闪烁(A级)
- 内容每秒闪烁不超过 3 次
- 闪烁区域较小(小于视口的 25%)
2.4 可导航性
2.4.1 跳过区块(A级)
- 提供“跳转到主要内容”链接
- 定义地标区域
- 标题结构正确
<a href="#main" class="skip-link">Skip to main content</a>
<main id="main">...</main>
2.4.2 页面标题(A级)
- 页面标题唯一且具有描述性
- 标题反映页面内容
2.4.3 焦点顺序(A级)
- 焦点顺序与视觉顺序一致
- 正确使用 tabindex
2.4.4 链接用途(结合上下文)(A 级)
- 链接脱离上下文后仍有明确含义
- 不单独使用“点击此处”或“阅读更多”
<a href="report.pdf">Click here</a>
<a href="report.pdf">Download Q4 Sales Report (PDF)</a>
2.4.6 标题和标签(AA 级)
- 标题能描述内容
- 标签能描述用途
2.4.7 焦点可见(AA 级)
- 所有元素上的焦点指示器都清晰可见
- 自定义焦点样式符合对比度要求
:focus {
outline: 3px solid #005fcc;
outline-offset: 2px;
}
2.4.11 焦点未被遮挡(AA 级)- WCAG 2.2
- 获得焦点的元素未被完全隐藏
- 粘性页眉不会遮挡焦点
### Understandable (Principle 3)
```markdown
## 3.1 Readable
### 3.1.1 Language of Page (Level A)
- [ ] HTML lang attribute set
- [ ] Language correct for content
```html
<html lang="en">
3.1.2 部分内容的语言(AA 级)
- 标记语言变更
<p>The French word <span lang="fr">bonjour</span> means hello.</p>
3.2 可预测
3.2.1 获得焦点时(A 级)
- 仅获得焦点不会导致上下文变化
- 获得焦点时不会出现意外弹窗
3.2.2 输入时(A 级)
- 不自动提交表单
- 上下文变化前向用户发出提示
3.2.3 一致的导航(AA 级)
- 各页面的导航保持一致
- 重复组件的顺序相同
3.2.4 一致的标识(AA 级)
- 相同功能使用相同标签
- 图标的使用保持一致
3.3 输入辅助
3.3.1 错误识别(A 级)
- 清晰标识错误
- 错误消息描述问题
- 错误与对应字段关联
<input aria-describedby="email-error" aria-invalid="true" />
<span id="email-error" role="alert">Please enter valid email</span>
3.3.2 标签或说明(A 级)
- 所有输入项都有可见标签
- 标明必填字段
- 提供格式提示
3.3.3 错误建议(AA 级)
- 错误信息包含更正建议
- 建议具体明确
3.3.4 错误预防(AA 级)
- 法律或财务表单可撤销
- 提交前检查数据
- 用户可以在提交前进行审核
### Robust (Principle 4)
```markdown
## 4.1 Compatible
### 4.1.1 Parsing (Level A) - Obsolete in WCAG 2.2
- [ ] Valid HTML (good practice)
- [ ] No duplicate IDs
- [ ] Complete start/end tags
### 4.1.2 Name, Role, Value (Level A)
- [ ] Custom widgets have accessible names
- [ ] ARIA roles correct
- [ ] State changes announced
```html
<div role="checkbox"
aria-checked="false"
tabindex="0"
aria-labelledby="label">
</div>
<span id="label">Accept terms</span>
4.1.3 状态消息(AA 级)
- 播报状态更新
- 正确使用实时区域
<div role="status" aria-live="polite">3 items added to cart</div>
<div role="alert" aria-live="assertive">Error: Form submission failed</div>
## Automated Testing
```javascript
// axe-core integration
const axe = require('axe-core');
async function runAccessibilityAudit(page) {
await page.addScriptTag({ path: require.resolve('axe-core') });
const results = await page.evaluate(async () => {
return await axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa']
}
});
});
return {
violations: results.violations,
passes: results.passes,
incomplete: results.incomplete
};
}
// Playwright test example
test('should have no accessibility violations', async ({ page }) => {
await page.goto('/');
const results = await runAccessibilityAudit(page);
expect(results.violations).toHaveLength(0);
});
# CLI tools
npx @axe-core/cli https://example.com
npx pa11y https://example.com
lighthouse https://example.com --only-categories=accessibility
修复模式
修复:缺少表单标签
<input type="email" placeholder="Email" />
<label for="email">Email address</label>
<input id="email" type="email" />
<input type="email" aria-label="Email address" />
<span id="email-label">Email</span>
<input type="email" aria-labelledby="email-label" />
修复:颜色对比度不足
/* Before: 2.5:1 contrast */
.text {
color: #767676;
}
/* After: 4.5:1 contrast */
.text {
color: #595959;
}
/* Or add background */
.text {
color: #767676;
background: #000;
}
修复:键盘导航
// Make custom element keyboard accessible
class AccessibleDropdown extends HTMLElement {
connectedCallback() {
this.setAttribute("tabindex", "0");
this.setAttribute("role", "combobox");
this.setAttribute("aria-expanded", "false");
this.addEventListener("keydown", (e) => {
switch (e.key) {
case "Enter":
case " ":
this.toggle();
e.preventDefault();
break;
case "Escape":
this.close();
break;
case "ArrowDown":
this.focusNext();
e.preventDefault();
break;
case "ArrowUp":
this.focusPrevious();
e.preventDefault();
break;
}
});
}
}
最佳实践
应该做
- 尽早开始 - 从设计阶段开始考虑无障碍性
- 使用真实用户进行测试 - 残障用户能提供最佳反馈
- 尽可能自动化 - 可检测出 30-50% 的问题
- 使用语义化 HTML - 减少对 ARIA 的需求
- 记录模式 - 构建无障碍组件库
不应该做
- 不要只依赖自动化测试 - 还需要手动测试
- 不要首先使用 ARIA 解决问题 - 优先使用原生 HTML
- 不要隐藏焦点轮廓 - 键盘用户需要它们
- 不要禁用缩放 - 用户需要调整大小
- 不要只使用颜色 - 需要多种指示方式
评论
支持 Markdown