网络可访问性审计Skill web-accessibility-audit

这个技能用于审计网络应用程序的可访问性,确保符合WCAG标准,识别常见违规并提供修复指导,适用于前端开发和测试场景。关键词包括:可访问性、WCAG、审计、测试、前端开发、质量保证。

测试 0 次安装 0 次浏览 更新于 3/15/2026

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万网站:

  1. 低颜色对比度(WCAG 1.4.3) - 86.4%的网站

    • 文本对比度<4.5:1
    • 大文本<3:1
    • UI组件<3:1
  2. 缺失/不充分的替代文本(WCAG 1.1.1) - 60.6%的网站

    • 图像无alt属性
    • 替代文本包含“图像”、“图片”、“照片”
    • 有意义的图像使用空alt
  3. 缺失名称、角色或值(WCAG 4.1.2)

    • 交互元素无可访问名称
    • 自定义组件无适当ARIA
    • 按钮、表单字段、自定义小部件
  4. 键盘导航失败(WCAG 2.1.1)

    • 有onClick但不可键盘访问的元素
    • 缺失焦点指示器
    • 键盘焦点被锁定
  5. 未标记的表单控件(WCAG 1.3.1, 3.3.2) - 39.6%的网站

    • 输入无<label>或aria-label
    • 标签未程序化关联
  6. 缺失语言属性(WCAG 3.1.1) - 28.9%的网站

    • <html>无lang属性
    • 缺失外语段落的lang
  7. 不当的标题结构(WCAG 1.3.1, 2.4.6)

    • 跳过的标题级别(h1→h3)
    • 多个h1或无h1
    • 空标题
  8. 空链接或差链接文本(WCAG 2.4.4)

    • 链接使用“点击这里”、“这里”、“阅读更多”
    • 空链接或仅图标链接
  9. 缺失/不当焦点指示器(WCAG 2.4.7)

    • CSS移除轮廓无替代
    • 焦点指示器对比度不足
  10. 过度/误用ARIA(WCAG 4.1.2)

    • 当原生HTML有效时使用不必要的ARIA
    • 角色无效的ARIA属性
    • 缺失必需的ARIA属性
  11. 不充分的数据表标记(WCAG 1.3.1)

    • 表格无<th>元素
    • 缺失scope或headers属性
  12. 缺失媒体字幕(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>

原因: [可访问性原则]


严重问题

[相同格式]

中等问题

[相同格式]

测试建议

  1. 手动键盘测试(Tab、Enter、Escape)
  2. 屏幕阅读器测试(见references/screen-reader-guide.md)
  3. 自动化测试设置(@axe-core/react或Lighthouse CI)
  4. 颜色对比度验证(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. 缺失语言属性