按 WCAG 标准审计 Web 应用无障碍问题并给出修复方法,涵盖自动扫描、键盘与屏幕阅读器测试、ARIA 和对比度检查。
填写变量
填写后,下方提示词与复制内容会同步替换。
---
name: accessibility-testing-superpower
description: |
执行 Web 应用程序的 WCAG 合规性审计和无障碍修复。
使用场景:1) 对 UI 进行 WCAG 2.1/2.2 合规性审计 2) 修复屏幕阅读器或键盘导航问题 3) 正确实现 ARIA 模式 4) 审查颜色对比度和视觉无障碍性 5) 创建无障碍表单或交互式组件
---
# 无障碍测试工作流
## 配置
- **WCAG 级别**: {{wcag_level}}
- **被测组件**: ${component_name:Page}
- **合规标准**: {{compliance_standard}}
- **最低 Lighthouse 分数**: {{lighthouse_score}}
- **主要屏幕阅读器**: {{screen_reader}}
- **测试框架**: {{test_framework}}
## 审计决策树
```
收到无障碍请求
|
+-- 新组件/页面?
| +-- 首先运行自动化扫描(axe-core, Lighthouse)
| +-- 键盘导航测试
| +-- 屏幕阅读器播报检查
| +-- 颜色对比度验证
|
+-- 存在需修复的现有违规?
| +-- 确定对应的 WCAG 成功准则
| +-- 检查语义化 HTML 是否可解决
| +-- 仅在 HTML 不足时应用 ARIA
| +-- 使用辅助技术验证修复效果
|
+-- 合规性审计?
+-- 自动化扫描(可捕获约 30% 的问题)
+-- 手动测试清单
+-- 按严重性记录违规项
+-- 创建修复路线图
```
## WCAG 快速参考
### 严重性分类
| 严重性 | 影响 | 示例 | 修复时间线 |
|----------|--------|----------|--------------|
| 致命 | 完全阻碍访问 | 无键盘焦点、空按钮、功能性图像缺少 alt | 立即 |
| 严重 | 重大障碍 | 对比度差、缺少表单标签、无跳过链接 | 本迭代内 |
| 中等 | 使用困难但仍可用 | 导航不一致、错误信息不明确 | 下一版本 |
| 轻微 | 使用不便 | 冗余的 alt 文本、轻微的标题层级问题 | 待办事项 |
### 常见违规及修复方法
**缺少可访问名称**
```html
<!-- 违规 -->
<button><svg>...</svg></button>
<!-- 修复:aria-label -->
<button aria-label="关闭对话框"><svg>...</svg></button>
<!-- 修复:视觉隐藏文本 -->
<button><span class="sr-only">关闭对话框</span><svg>...</svg></button>
```
**表单标签关联**
```html
<!-- 违规 -->
<label>邮箱</label>
<input type="email">
<!-- 修复:显式关联 -->
<label for="email">邮箱</label>
<input type="email" id="email">
<!-- 修复:隐式关联 -->
<label>邮箱 <input type="email"></label>
```
**颜色对比度不达标**
```
最低对比度比例(WCAG {{wcag_level}}):
- 普通文本(<${large_text_size:18}px 或 <${bold_text_size:14}px 加粗):${contrast_ratio_normal:4.5}:1
- 大号文本(>=${large_text_size:18}px 或 >=${bold_text_size:14}px 加粗):${contrast_ratio_large:3}:1
- UI 组件和图形:3:1
工具:WebAIM Contrast Checker、浏览器 DevTools
```
**焦点可见性**
```css
/* 不提供替代方案时切勿这样做 */
:focus { outline: none; }
/* 正确的自定义焦点样式 */
:focus-visible {
outline: ${focus_outline_width:2}px solid ${focus_outline_color:#005fcc};
outline-offset: ${focus_outline_offset:2}px;
}
```
### 焦点管理模式
**模态框焦点陷阱**
```javascript
// 模态框打开时:
// 1. 保存之前获得焦点的元素
// 2. 将焦点移动到模态框内第一个可聚焦元素
// 3. 将 Tab 键限制在模态框边界内
// 模态框关闭时:
// 1. 将焦点返回至已保存的元素
```
**动态内容**
```javascript
// 添加内容后:
// - 通过 aria-live 区域宣布,或
// - 将焦点移至新内容的标题
// 删除内容后:
// - 将焦点移至逻辑上的下一个元素
// - 绝不能让焦点停留在已被删除的元素上
```
## 屏幕阅读器测试
### 宣告内容验证
| 元素 | 应当宣告的内容 |
|---------|-----------------|
| 按钮 | 角色 + 名称 + 状态(例如“提交按钮”) |
| 链接 | 名称 + “link”(例如“首页链接”) |
| 图像 | 替代文本 或 “decorative”(跳过) |
| 标题 | 级别 + 文本(例如“二级标题,关于我们”) |
| 表单字段 | 标签 + 类型 + 状态 + 说明 |
| 错误 | 错误信息 + 关联字段 |
### 测试命令(快速参考)
**VoiceOver (macOS)**
- VO = Ctrl + Option
- VO + A:朗读全部
- VO + Right/Left:在元素间导航
- VO + Cmd + H:下一个标题
- VO + Cmd + J:下一个表单控件
**{{screen_reader}} (Windows)**
- NVDA + Down:朗读全部
- Tab:下一个可聚焦元素
- H:下一个标题
- F:下一个表单字段
- B:下一个按钮
## 自动化测试集成
### 在测试中使用 axe-core
```javascript
// {{test_framework}}
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('${component_name:component} 是无障碍的', async () => {
const { container } = render(<${component_name:MyComponent} />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
```
### Lighthouse CI 门槛设置
```javascript
// lighthouserc.js
module.exports = {
assertions: {
'categories:accessibility': ['error', { minScore: {{lighthouse_score}} / 100 }],
},
};
```
## 修复优先级矩阵
```
影响 vs 努力:
低努力 高努力
高影响 | 优先处理 | 计划后续 |
| 替代文本 | 重新设计 |
| 标签 | 导航重构 |
-----------------|---------------|-------------|
低影响 | 快速胜利 | 待办清单 |
| 对比度调整 | 可选优化 |
| | 增强功能 |
```
## 验证清单
在标记无障碍工作完成前:
```
自动化测试:
[ ] axe-core 报告零违规
[ ] Lighthouse 无障碍分数 >= {{lighthouse_score}}
[ ] HTML 验证通过(影响辅助技术解析)
键盘测试:
[ ] 不使用鼠标即可完成全部任务
[ ] 始终可见焦点
[ ] Tab 键顺序符合逻辑
[ ] 无焦点陷阱
屏幕阅读器测试:
[ ] 至少使用一种屏幕阅读器进行测试 ({{screen_reader}})
[ ] 所有内容均被正确宣告
[ ] 交互元素具有正确的角色/状态
[ ] 动态更新被正确宣告
视觉测试:
[ ] 已验证对比度比率 (${contrast_ratio_normal:4.5}:1 最低要求)
[ ] 在 ${zoom_level:200}% 缩放下正常工作
[ ] 无仅通过颜色传达的信息
[ ] 尊重 prefers-reduced-motion 设置
```---
name: accessibility-testing-superpower
description: |
Performs WCAG compliance audits and accessibility remediation for web applications.
Use when: 1) Auditing UI for WCAG 2.1/2.2 compliance 2) Fixing screen reader or keyboard navigation issues 3) Implementing ARIA patterns correctly 4) Reviewing color contrast and visual accessibility 5) Creating accessible forms or interactive components
---
# Accessibility Testing Workflow
## Configuration
- **WCAG Level**: {{wcag_level}}
- **Component Under Test**: ${component_name:Page}
- **Compliance Standard**: {{compliance_standard}}
- **Minimum Lighthouse Score**: {{lighthouse_score}}
- **Primary Screen Reader**: {{screen_reader}}
- **Test Framework**: {{test_framework}}
## Audit Decision Tree
```
Accessibility request received
|
+-- New component/page?
| +-- Run automated scan first (axe-core, Lighthouse)
| +-- Keyboard navigation test
| +-- Screen reader announcement check
| +-- Color contrast verification
|
+-- Existing violation to fix?
| +-- Identify WCAG success criterion
| +-- Check if semantic HTML solves it
| +-- Apply ARIA only when HTML insufficient
| +-- Verify fix with assistive technology
|
+-- Compliance audit?
+-- Automated scan (catches ~30% of issues)
+-- Manual testing checklist
+-- Document violations by severity
+-- Create remediation roadmap
```
## WCAG Quick Reference
### Severity Classification
| Severity | Impact | Examples | Fix Timeline |
|----------|--------|----------|--------------|
| Critical | Blocks access entirely | No keyboard focus, empty buttons, missing alt on functional images | Immediate |
| Serious | Major barriers | Poor contrast, missing form labels, no skip links | Within sprint |
| Moderate | Difficult but usable | Inconsistent navigation, unclear error messages | Next release |
| Minor | Inconvenience | Redundant alt text, minor heading order issues | Backlog |
### Common Violations and Fixes
**Missing accessible name**
```html
<!-- Violation -->
<button><svg>...</svg></button>
<!-- Fix: aria-label -->
<button aria-label="Close dialog"><svg>...</svg></button>
<!-- Fix: visually hidden text -->
<button><span class="sr-only">Close dialog</span><svg>...</svg></button>
```
**Form label association**
```html
<!-- Violation -->
<label>Email</label>
<input type="email">
<!-- Fix: explicit association -->
<label for="email">Email</label>
<input type="email" id="email">
<!-- Fix: implicit association -->
<label>Email <input type="email"></label>
```
**Color contrast failure**
```
Minimum ratios (WCAG {{wcag_level}}):
- Normal text (<${large_text_size:18}px or <${bold_text_size:14}px bold): ${contrast_ratio_normal:4.5}:1
- Large text (>=${large_text_size:18}px or >=${bold_text_size:14}px bold): ${contrast_ratio_large:3}:1
- UI components and graphics: 3:1
Tools: WebAIM Contrast Checker, browser DevTools
```
**Focus visibility**
```css
/* Never do this without alternative */
:focus { outline: none; }
/* Proper custom focus */
:focus-visible {
outline: ${focus_outline_width:2}px solid ${focus_outline_color:#005fcc};
outline-offset: ${focus_outline_offset:2}px;
}
```
## ARIA Decision Framework
```
Need to convey information to assistive technology?
|
+-- Can semantic HTML do it?
| +-- YES: Use HTML (<button>, <nav>, <main>, <article>)
| +-- NO: Continue to ARIA
|
+-- What type of ARIA needed?
+-- Role: What IS this element? (role="dialog", role="tab")
+-- State: What condition? (aria-expanded, aria-checked)
+-- Property: What relationship? (aria-labelledby, aria-describedby)
+-- Live region: Dynamic content? (aria-live="${aria_live_mode:polite}")
```
### ARIA Patterns for Common Widgets
**Disclosure (show/hide)**
```html
<button aria-expanded="false" aria-controls="content-1">
Show details
</button>
<div id="content-1" hidden>
Content here
</div>
```
**Tab interface**
```html
<div role="tablist" aria-label="${component_name:Settings}">
<button role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1">
General
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">
Privacy
</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>
```
**Modal dialog**
```html
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm action</h2>
<p>Are you sure you want to proceed?</p>
<button>Cancel</button>
<button>Confirm</button>
</div>
```
## Keyboard Navigation Checklist
```
[ ] All interactive elements focusable with Tab
[ ] Focus order matches visual/logical order
[ ] Focus visible on all elements
[ ] No keyboard traps (can always Tab out)
[ ] Skip link as first focusable element
[ ] Escape closes modals/dropdowns
[ ] Arrow keys navigate within widgets (tabs, menus, grids)
[ ] Enter/Space activates buttons and links
[ ] Custom shortcuts documented and configurable
```
### Focus Management Patterns
**Modal focus trap**
```javascript
// On modal open:
// 1. Save previously focused element
// 2. Move focus to first focusable in modal
// 3. Trap Tab within modal boundaries
// On modal close:
// 1. Return focus to saved element
```
**Dynamic content**
```javascript
// After adding content:
// - Announce via aria-live region, OR
// - Move focus to new content heading
// After removing content:
// - Move focus to logical next element
// - Never leave focus on removed element
```
## Screen Reader Testing
### Announcement Verification
| Element | Should Announce |
|---------|-----------------|
| Button | Role + name + state ("Submit button") |
| Link | Name + "link" ("Home page link") |
| Image | Alt text OR "decorative" (skip) |
| Heading | Level + text ("Heading level 2, About us") |
| Form field | Label + type + state + instructions |
| Error | Error message + field association |
### Testing Commands (Quick Reference)
**VoiceOver (macOS)**
- VO = Ctrl + Option
- VO + A: Read all
- VO + Right/Left: Navigate elements
- VO + Cmd + H: Next heading
- VO + Cmd + J: Next form control
**{{screen_reader}} (Windows)**
- NVDA + Down: Read all
- Tab: Next focusable
- H: Next heading
- F: Next form field
- B: Next button
## Automated Testing Integration
### axe-core in tests
```javascript
// {{test_framework}}
import { axe, toHaveNoViolations } from 'jest-axe';
expect.extend(toHaveNoViolations);
test('${component_name:component} is accessible', async () => {
const { container } = render(<${component_name:MyComponent} />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
```
### Lighthouse CI threshold
```javascript
// lighthouserc.js
module.exports = {
assertions: {
'categories:accessibility': ['error', { minScore: {{lighthouse_score}} / 100 }],
},
};
```
## Remediation Priority Matrix
```
Impact vs Effort:
Low Effort High Effort
High Impact | DO FIRST | PLAN NEXT |
| alt text | redesign |
| labels | nav rebuild |
----------------|--------------|---------------|
Low Impact | QUICK WIN | BACKLOG |
| contrast | nice-to-have|
| tweaks | enhancements|
```
## Verification Checklist
Before marking accessibility work complete:
```
Automated Testing:
[ ] axe-core reports zero violations
[ ] Lighthouse accessibility >= {{lighthouse_score}}
[ ] HTML validator passes (affects AT parsing)
Keyboard Testing:
[ ] Full task completion without mouse
[ ] Visible focus at all times
[ ] Logical tab order
[ ] No traps
Screen Reader Testing:
[ ] Tested with at least one screen reader ({{screen_reader}})
[ ] All content announced correctly
[ ] Interactive elements have roles/states
[ ] Dynamic updates announced
Visual Testing:
[ ] Contrast ratios verified (${contrast_ratio_normal:4.5}:1 minimum)
[ ] Works at ${zoom_level:200}% zoom
[ ] No information conveyed by color alone
[ ] Respects prefers-reduced-motion
```