审计并修复 WCAG 无障碍问题,涵盖键盘导航、屏幕阅读器、对比度、表单与 ARIA。
---
name: accessibility-expert
description: 测试并修复无障碍问题,以符合 WCAG 标准并兼容辅助技术。适用于以下场景:(1) 审计用户界面的无障碍违规问题,(2) 实现键盘导航或屏幕阅读器支持,(3) 修复颜色对比度或焦点指示器问题,(4) 确保表单的无障碍性及错误处理,(5) 创建 ARIA 实现。
---
# 无障碍测试与修复
## 配置
- **WCAG 等级**:${wcag_level:AA}
- **目标组件**:${component_name:Application}
- **合规标准**:${compliance_standard:WCAG 2.1}
- **测试范围**:${testing_scope:full-audit}
- **屏幕阅读器**:${screen_reader:NVDA}
## WCAG 2.1 快速参考
### 合规等级
| 等级 | 要求 | 常见问题 |
|-------|-------------|---------------|
| A | 最低基准 | 缺少替代文本、无键盘访问、缺少表单标签 |
| ${wcag_level:AA} | 标准目标 | 对比度 < 4.5:1、缺少焦点指示器、标题结构不佳 |
| AAA | 增强 | 对比度 < 7:1、手语支持、扩展音频描述 |
### 四大原则(POUR)
1. **可感知(Perceivable)**:内容可通过感官获取(替代文本、字幕、对比度)
2. **可操作(Operable)**:用户界面可通过所有输入方式导航(键盘、触摸、语音)
3. **可理解(Understandable)**:内容和用户界面具有可预测性和可读性
4. **稳健性(Robust)**:可与当前及未来的辅助技术兼容
## 违规严重性矩阵
```
CRITICAL(立即修复):
- 交互元素无法通过键盘访问
- 缺少表单标签
- 图像缺少替代文本
- 自动播放音频且无控制功能
- 键盘陷阱
HIGH(发布前修复):
- 对比度低于 ${min_contrast_ratio:4.5}:1(普通文本)或 3:1(大文本)
- 缺少跳转链接
- 标题层级不正确
- 焦点不可见
- 缺少错误识别
MEDIUM(下个冲刺周期修复):
- 导航不一致
- 缺少区域地标
- 链接文本不明确(如“点击这里”)
- 缺少语言属性
- 复杂表格缺少表头
LOW(列入待办事项):
- 时间调整
- 多种方式查找内容
- 上下文相关帮助
```
## 测试决策树
```
开始:你要测试什么?
|
+-- 新组件
| +-- 包含交互元素? --> 键盘导航检查清单
| +-- 包含文本内容? --> 检查对比度 + 标题结构
| +-- 包含图像? --> 验证替代文本是否恰当
| +-- 包含表单? --> 表单无障碍性检查清单
|
+-- 现有页面/功能
| +-- 首先运行自动化扫描(axe-core, Lighthouse)
| +-- 手动进行键盘遍历
| +-- 屏幕阅读器验证
| +-- 抽查颜色对比度
|
+-- 第三方控件
+-- 检查 ARIA 实现
+-- 验证键盘支持
+-- 使用屏幕阅读器测试
+-- 记录限制
```
## 键盘导航检查清单
```markdown
[ ] 所有交互元素可通过 Tab 键访问
[ ] Tab 顺序遵循视觉/逻辑流程
[ ] 焦点指示器可见(${focus_indicator_width:2}px+ 轮廓,对比度 3:1)
[ ] 无键盘陷阱(可从所有元素中 Tab 出去)
[ ] 跳转链接为首个可聚焦元素
[ ] Enter 键激活按钮和链接
[ ] Space 键激活复选框和按钮
[ ] 箭头键在组件内导航(标签页、菜单、单选组)
[ ] Escape 键关闭模态框和下拉菜单
[ ] 模态框在关闭前应限制焦点
```
## 屏幕阅读器测试模式
### 需验证的关键播报内容
```
交互元素:
按钮:"[标签],按钮"
链接:"[文本],链接"
复选框:"[标签],复选框,[已选/未选]"
单选按钮:"[标签],单选按钮,[已选],第[position]个共[total]个"
组合框:"[标签],组合框,[已折叠/已展开]"
动态内容:
加载中:在容器上使用 aria-busy="true"
状态:对非关键更新使用 role="status"
警报:对关键消息使用 role="alert"
实时区域:aria-live="${aria_live_politeness:polite}"
表单:
必填项:标签中播报“required”
无效输入:播报“invalid entry”并附带错误信息
说明:通过 aria-describedby 与标签关联后播报
```
### 测试流程
1. 使用 Tab 键浏览整个页面,监听播报内容
2. 测试标题导航(屏幕阅读器中的 H 键)
3. 测试区域地标导航(D 键 / 转子)
4. 测试表格(T 键,表格内使用箭头键)
5. 测试表单(F 键,完成表单提交)
6. 测试动态内容更新(验证实时区域)
## 颜色对比度要求
| 文本类型 | 最低对比度 | 增强(AAA) |
|-----------|---------------|----------------|
| 普通文本(<${large_text_threshold:18}pt) | ${min_contrast_ratio:4.5}:1 | 7:1 |
| 大文本(>=${large_text_threshold:18}pt 或 14pt 加粗) | 3:1 | 4.5:1 |
| UI 组件与图形 | 3:1 | N/A |
| 焦点指示器 | 3:1 | N/A |
### 对比度检查流程
```
1. 识别所有前景色/背景色组合
2. 计算对比度:(L1 + 0.05) / (L2 + 0.05)
其中 L1 = 较亮的亮度,L2 = 较暗的亮度
3. 需重点检查的常见失败项:
- 占位符文本(通常颜色太浅)
- 禁用状态(可豁免但需考虑可用性)
- 文本中的链接(必须与普通文本区分开)
- 彩色背景上的错误/成功状态
- 图像上的文字(使用叠加层或文字阴影)
```
## ARIA 实现指南
### ARIA 第一准则
尽可能使用原生 HTML 元素。ARIA 仅用于自定义控件。
```html
<!-- 错误:在原生元素上使用 ARIA -->
<div role="button" tabindex="0">提交</div>
<!-- 正确:使用原生按钮 -->
<button type="submit">提交</button>
```
### 需要使用 ARIA 的场景
```html
<!-- 自定义标签页 -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">标签页 1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">标签页 2</button>
</div>
<div role="tabpanel" id="panel1">内容 1</div>
<div role="tabpanel" id="panel2" hidden>内容 2</div>
<!-- 可展开区域 -->
<button aria-expanded="false" aria-controls="content">显示详情</button>
<div id="content" hidden>可展开内容</div>
<!-- 模态对话框 -->
<div role="dialog" aria-modal="true" aria-labelledby="title">
<h2 id="title">对话框标题</h2>
<!-- 内容 -->
</div>
<!-- 用于动态更新的实时区域 -->
<div aria-live="${aria_live_politeness:polite}" aria-atomic="true">
<!-- 状态消息将注入此处 -->
</div>
```
### 常见 ARIA 错误
```
- 使用 role="button" 但未提供键盘支持(Enter/Space)
- aria-label 重复可见文本
- 在可聚焦元素上使用 aria-hidden="true"
- 折叠按钮缺少 aria-expanded
- aria-controls 引用错误
- 使用 aria-describedby 传递关键信息
```
## 表单无障碍模式
### 必需的表单结构
```html
<form>
<!-- 显式关联标签 -->
<label for="email">电子邮件地址</label>
<input type="email" id="email" name="email"
aria-required="true"
aria-describedby="email-hint email-error">
<span id="email-hint">我们绝不会共享您的电子邮件</span>
<span id="email-error" role="alert"></span>
<!-- 将相关字段分组 -->
<fieldset>
<legend>收货地址</legend>
<!-- 地址字段 -->
</fieldset>
<!-- 明确的提交按钮 -->
<button type="submit">完成订单</button>
</form>
```
### 错误处理要求
```
1. 标识出错字段(高亮 + 图标)
2. 用文本描述错误(不能仅靠颜色)
3. 将错误与字段关联(aria-describedby)
4. 向屏幕阅读器播报错误(role="alert")
5. 提交失败时将焦点移至首个错误
6. 尽可能提供修正建议
```
## 移动端无障碍检查清单
```markdown
触控目标:
[ ] 最小 ${touch_target_size:44}x${touch_target_size:44} CSS 像素
[ ] 目标之间有足够间距(${touch_target_spacing:8}px+)
[ ] 触控操作不依赖手势路径
手势:
[ ] 提供多指手势的替代方式
[ ] 提供基于路径手势(如滑动)的替代方式
[ ] 基于动作的操作有替代方案
屏幕阅读器(iOS/Android):
[ ] 为图像和图标设置 accessibilityLabel
[ ] 为复杂交互设置 accessibilityHint
[ ] accessibilityRole 与元素行为匹配
[ ] 焦点顺序符合视觉布局
```
## 自动化测试集成
### 提交前钩子
```bash
#!/bin/bash
# 在变更的文件上运行 axe-core
npx axe-core-cli --exit src/**/*.html
# 检查常见问题
grep -r "onClick.*div\|onClick.*span" src/ && \
echo "警告:非交互元素上添加了点击处理器" && exit 1
```
### CI 流水线检查
```yaml
accessibility-audit:
script:
- npx pa11y-ci --config .pa11yci.json
- npx lighthouse --accessibility --output=json
artifacts:
paths:
- accessibility-report.json
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
```
### 最低 CI 阈值
```
axe-core:0 个严重违规,0 个重大违规
Lighthouse 无障碍评分:>= ${lighthouse_a11y_threshold:90}
pa11y:0 个错误(警告可接受)
```
## 修复优先级框架
```
优先级 1(本冲刺):
- 阻碍用户完成任务
- 存在法律合规风险
- 影响大量用户
优先级 2(下一冲刺):
- 显著降低用户体验
- 自动化工具标记为错误
- 违反 ${wcag_level:AA} 要求
优先级 3(待办事项):
- 轻微不便
- 仅违反 AAA 级要求
- 影响边缘情况
优先级 4(增强):
- 提升所有用户的可用性
- 最佳实践,非强制要求
- 为未来做准备
```
## 验证检查清单
在标记无障碍工作完成前:
```markdown
自动化:
[ ] axe-core:0 项违规
[ ] Lighthouse 无障碍评分:${lighthouse_a11y_threshold:90}+
[ ] HTML 验证通过
[ ] 无控制台无障碍警告
键盘:
[ ] 可仅使用键盘完成所有任务
[ ] 始终可见焦点
[ ] Tab 顺序合理
[ ] 无键盘陷阱
屏幕阅读器(至少测试一种):
[ ] 所有内容均被播报
[ ] 交互元素有标签
[ ] 错误和更新被播报
[ ] 导航高效
```
视觉:
[ ] 所有文本通过对比度检测
[ ] 用户界面组件通过对比度检测
[ ] 在 ${zoom_level:200}% 缩放下可用
[ ] 支持高对比度模式
[ ] 无引发癫痫的闪烁内容
表单:
[ ] 所有字段均已标注
[ ] 错误可识别
[ ] 已标明必填字段
[ ] 提供操作说明
```
## 文档模板
```markdown
# 可访问性声明
## 符合性状态
本 [网站/应用] [完全/部分] 符合 ${compliance_standard:WCAG 2.1} 级别 ${wcag_level:AA} 的要求。
## 已知限制
| 功能 | 问题 | 替代方案 | 时间线 |
|---------|-------|------------|----------|
| [功能] | [描述] | [替代方案] | [修复日期] |
## 已测试的辅助技术
- ${screen_reader:NVDA} [版本] 与 Firefox [版本]
- VoiceOver 与 Safari [版本]
- JAWS [版本] 与 Chrome [版本]
## 反馈
如有可访问性问题,请联系 [邮箱]。
最后更新日期: [日期]
```---
name: accessibility-expert
description: Tests and remediates accessibility issues for WCAG compliance and assistive technology compatibility. Use when (1) auditing UI for accessibility violations, (2) implementing keyboard navigation or screen reader support, (3) fixing color contrast or focus indicator issues, (4) ensuring form accessibility and error handling, (5) creating ARIA implementations.
---
# Accessibility Testing and Remediation
## Configuration
- **WCAG Level**: ${wcag_level:AA}
- **Target Component**: ${component_name:Application}
- **Compliance Standard**: ${compliance_standard:WCAG 2.1}
- **Testing Scope**: ${testing_scope:full-audit}
- **Screen Reader**: ${screen_reader:NVDA}
## WCAG 2.1 Quick Reference
### Compliance Levels
| Level | Requirement | Common Issues |
|-------|-------------|---------------|
| A | Minimum baseline | Missing alt text, no keyboard access, missing form labels |
| ${wcag_level:AA} | Standard target | Contrast < 4.5:1, missing focus indicators, poor heading structure |
| AAA | Enhanced | Contrast < 7:1, sign language, extended audio description |
### Four Principles (POUR)
1. **Perceivable**: Content available to senses (alt text, captions, contrast)
2. **Operable**: UI navigable by all input methods (keyboard, touch, voice)
3. **Understandable**: Content and UI predictable and readable
4. **Robust**: Works with current and future assistive technologies
## Violation Severity Matrix
```
CRITICAL (fix immediately):
- No keyboard access to interactive elements
- Missing form labels
- Images without alt text
- Auto-playing audio without controls
- Keyboard traps
HIGH (fix before release):
- Contrast ratio below ${min_contrast_ratio:4.5}:1 (text) or 3:1 (large text)
- Missing skip links
- Incorrect heading hierarchy
- Focus not visible
- Missing error identification
MEDIUM (fix in next sprint):
- Inconsistent navigation
- Missing landmarks
- Poor link text ("click here")
- Missing language attribute
- Complex tables without headers
LOW (backlog):
- Timing adjustments
- Multiple ways to find content
- Context-sensitive help
```
## Testing Decision Tree
```
Start: What are you testing?
|
+-- New Component
| +-- Has interactive elements? --> Keyboard Navigation Checklist
| +-- Has text content? --> Check contrast + heading structure
| +-- Has images? --> Verify alt text appropriateness
| +-- Has forms? --> Form Accessibility Checklist
|
+-- Existing Page/Feature
| +-- Run automated scan first (axe-core, Lighthouse)
| +-- Manual keyboard walkthrough
| +-- Screen reader verification
| +-- Color contrast spot-check
|
+-- Third-party Widget
+-- Check ARIA implementation
+-- Verify keyboard support
+-- Test with screen reader
+-- Document limitations
```
## Keyboard Navigation Checklist
```markdown
[ ] All interactive elements reachable via Tab
[ ] Tab order follows visual/logical flow
[ ] Focus indicator visible (${focus_indicator_width:2}px+ outline, 3:1 contrast)
[ ] No keyboard traps (can Tab out of all elements)
[ ] Skip link as first focusable element
[ ] Enter activates buttons and links
[ ] Space activates checkboxes and buttons
[ ] Arrow keys navigate within components (tabs, menus, radio groups)
[ ] Escape closes modals and dropdowns
[ ] Modals trap focus until dismissed
```
## Screen Reader Testing Patterns
### Essential Announcements to Verify
```
Interactive Elements:
Button: "[label], button"
Link: "[text], link"
Checkbox: "[label], checkbox, [checked/unchecked]"
Radio: "[label], radio button, [selected], [position] of [total]"
Combobox: "[label], combobox, [collapsed/expanded]"
Dynamic Content:
Loading: Use aria-busy="true" on container
Status: Use role="status" for non-critical updates
Alert: Use role="alert" for critical messages
Live regions: aria-live="${aria_live_politeness:polite}"
Forms:
Required: "required" announced with label
Invalid: "invalid entry" with error message
Instructions: Announced with label via aria-describedby
```
### Testing Sequence
1. Navigate entire page with Tab key, listening to announcements
2. Test headings navigation (H key in screen reader)
3. Test landmark navigation (D key / rotor)
4. Test tables (T key, arrow keys within table)
5. Test forms (F key, complete form submission)
6. Test dynamic content updates (verify live regions)
## Color Contrast Requirements
| Text Type | Minimum Ratio | Enhanced (AAA) |
|-----------|---------------|----------------|
| Normal text (<${large_text_threshold:18}pt) | ${min_contrast_ratio:4.5}:1 | 7:1 |
| Large text (>=${large_text_threshold:18}pt or 14pt bold) | 3:1 | 4.5:1 |
| UI components & graphics | 3:1 | N/A |
| Focus indicators | 3:1 | N/A |
### Contrast Check Process
```
1. Identify all foreground/background color pairs
2. Calculate contrast ratio: (L1 + 0.05) / (L2 + 0.05)
where L1 = lighter luminance, L2 = darker luminance
3. Common failures to check:
- Placeholder text (often too light)
- Disabled state (exempt but consider usability)
- Links within text (must distinguish from text)
- Error/success states on colored backgrounds
- Text over images (use overlay or text shadow)
```
## ARIA Implementation Guide
### First Rule of ARIA
Use native HTML elements when possible. ARIA is for custom widgets only.
```html
<!-- WRONG: ARIA on native element -->
<div role="button" tabindex="0">Submit</div>
<!-- RIGHT: Native button -->
<button type="submit">Submit</button>
```
### When ARIA is Needed
```html
<!-- Custom tabs -->
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel1">Tab 1</button>
<button role="tab" aria-selected="false" aria-controls="panel2">Tab 2</button>
</div>
<div role="tabpanel" id="panel1">Content 1</div>
<div role="tabpanel" id="panel2" hidden>Content 2</div>
<!-- Expandable section -->
<button aria-expanded="false" aria-controls="content">Show details</button>
<div id="content" hidden>Expandable content</div>
<!-- Modal dialog -->
<div role="dialog" aria-modal="true" aria-labelledby="title">
<h2 id="title">Dialog Title</h2>
<!-- content -->
</div>
<!-- Live region for dynamic updates -->
<div aria-live="${aria_live_politeness:polite}" aria-atomic="true">
<!-- Status messages injected here -->
</div>
```
### Common ARIA Mistakes
```
- role="button" without keyboard support (Enter/Space)
- aria-label duplicating visible text
- aria-hidden="true" on focusable elements
- Missing aria-expanded on disclosure buttons
- Incorrect aria-controls reference
- Using aria-describedby for essential information
```
## Form Accessibility Patterns
### Required Form Structure
```html
<form>
<!-- Explicit label association -->
<label for="email">Email address</label>
<input type="email" id="email" name="email"
aria-required="true"
aria-describedby="email-hint email-error">
<span id="email-hint">We'll never share your email</span>
<span id="email-error" role="alert"></span>
<!-- Group related fields -->
<fieldset>
<legend>Shipping address</legend>
<!-- address fields -->
</fieldset>
<!-- Clear submit button -->
<button type="submit">Complete order</button>
</form>
```
### Error Handling Requirements
```
1. Identify the field in error (highlight + icon)
2. Describe the error in text (not just color)
3. Associate error with field (aria-describedby)
4. Announce error to screen readers (role="alert")
5. Move focus to first error on submit failure
6. Provide correction suggestions when possible
```
## Mobile Accessibility Checklist
```markdown
Touch Targets:
[ ] Minimum ${touch_target_size:44}x${touch_target_size:44} CSS pixels
[ ] Adequate spacing between targets (${touch_target_spacing:8}px+)
[ ] Touch action not dependent on gesture path
Gestures:
[ ] Alternative to multi-finger gestures
[ ] Alternative to path-based gestures (swipe)
[ ] Motion-based actions have alternatives
Screen Reader (iOS/Android):
[ ] accessibilityLabel set for images and icons
[ ] accessibilityHint for complex interactions
[ ] accessibilityRole matches element behavior
[ ] Focus order follows visual layout
```
## Automated Testing Integration
### Pre-commit Hook
```bash
#!/bin/bash
# Run axe-core on changed files
npx axe-core-cli --exit src/**/*.html
# Check for common issues
grep -r "onClick.*div\|onClick.*span" src/ && \
echo "Warning: Click handler on non-interactive element" && exit 1
```
### CI Pipeline Checks
```yaml
accessibility-audit:
script:
- npx pa11y-ci --config .pa11yci.json
- npx lighthouse --accessibility --output=json
artifacts:
paths:
- accessibility-report.json
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
```
### Minimum CI Thresholds
```
axe-core: 0 critical violations, 0 serious violations
Lighthouse accessibility: >= ${lighthouse_a11y_threshold:90}
pa11y: 0 errors (warnings acceptable)
```
## Remediation Priority Framework
```
Priority 1 (This Sprint):
- Blocks user task completion
- Legal compliance risk
- Affects many users
Priority 2 (Next Sprint):
- Degrades experience significantly
- Automated tools flag as error
- Violates ${wcag_level:AA} requirement
Priority 3 (Backlog):
- Minor inconvenience
- Violates AAA only
- Affects edge cases
Priority 4 (Enhancement):
- Improves usability for all
- Best practice, not requirement
- Future-proofing
```
## Verification Checklist
Before marking accessibility work complete:
```markdown
Automated:
[ ] axe-core: 0 violations
[ ] Lighthouse accessibility: ${lighthouse_a11y_threshold:90}+
[ ] HTML validation passes
[ ] No console accessibility warnings
Keyboard:
[ ] Complete all tasks keyboard-only
[ ] Focus visible at all times
[ ] Tab order logical
[ ] No keyboard traps
Screen Reader (test with at least one):
[ ] All content announced
[ ] Interactive elements labeled
[ ] Errors and updates announced
[ ] Navigation efficient
Visual:
[ ] All text passes contrast
[ ] UI components pass contrast
[ ] Works at ${zoom_level:200}% zoom
[ ] Works in high contrast mode
[ ] No seizure-inducing flashing
Forms:
[ ] All fields labeled
[ ] Errors identifiable
[ ] Required fields indicated
[ ] Instructions available
```
## Documentation Template
```markdown
# Accessibility Statement
## Conformance Status
This [website/application] is [fully/partially] conformant with ${compliance_standard:WCAG 2.1} Level ${wcag_level:AA}.
## Known Limitations
| Feature | Issue | Workaround | Timeline |
|---------|-------|------------|----------|
| [Feature] | [Description] | [Alternative] | [Fix date] |
## Assistive Technology Tested
- ${screen_reader:NVDA} [version] with Firefox [version]
- VoiceOver with Safari [version]
- JAWS [version] with Chrome [version]
## Feedback
Contact [email] for accessibility issues.
Last updated: [date]
```