> Adapted from alirezarezvani/claude-skills by Alireza Rezvani (MIT License)。
> 本技能为功能性改编:保留上游「WCAG 2.2 三阶段 Scan-Fix-Verify + 框架专属修复 + 合规报告」的方法论,重写为平台无关的中文工作流,并附两个纯 Python 标准库、零依赖脚本(scripts/a11y_scanner.py、scripts/contrast_checker.py);剥离了上游闭源参考文档与特定框架绑定,改为可直接套用的通用规则与对比度算法。
WCAG 无障碍静态审查
何时使用
- 给代码库做无障碍体检,定位 WCAG 2.2 Level A/AA 违规;
- 修复已知 a11y 问题,并验证修复未引入回归;
- 把无障碍检查接进 CI/CD,防止回归;
- 计算前景/背景颜色对比度是否达到 AA/AAA。
铁律:静态扫描是起点而非终点——它无法替代真实屏幕阅读器与浏览器键盘测试,结果应与人工复核(见下方「人工复核清单」)配合。
三阶段工作流
阶段 1 · Scan(扫描)——遍历源码,自动探测框架(React/Nextjs/Vue/Angular/Svelte/纯 HTML),按规则集输出问题清单(按 Critical/Major/Minor 分级)。
python scripts/a11y_scanner.py /path/to/project --format table
python scripts/a11y_scanner.py /path/to/project --format json --severity critical,major
阶段 2 · Fix(修复)——对每条违规给出对应框架的 before/after 修复示例(见下方「常见陷阱与正确做法」)。
阶段 3 · Verify(验证)——重新扫描确认修复,并用 --baseline 对比上次结果,确认无回归。
python scripts/a11y_scanner.py /path/to/project --baseline prev.json --ci # 有 critical 则非零退出
严重度定义
| 严重度 | 含义 | 示例 | SLA |
|---|---|---|---|
| Critical | 整类用户无法使用 | 图片缺 alt、导航无法键盘操作 | 发布前必须修复 |
| Major | 显著障碍、体验下降 | 对比度不足、表单缺 label | 当前迭代内修复 |
| Minor | 可用性摩擦 | 冗余 ARIA、标题层级不完美 | 下个迭代内修复 |
扫描覆盖的规则(a11y_scanner.py)
- 图片 alt:
<img>缺少alt(Critical,WCAG 1.1.1)。 - 表单 label:
<input>缺少label关联或aria-label/aria-labelledby(Critical,1.3.1/4.1.2)。 - 键盘可达:
<div>/<span>/<a>的onClick没有onKeyDown或role(Critical,2.1.1)。 - ARIA 误用:
role="button"却无键盘处理/无tabIndex(Major,2.1.1)。 - 正 tabindex:
tabIndex={>0}破坏自然焦点顺序(Major,2.4.3)。 - 空链接:
<a>无可访问名称(Major,2.4.4/4.1.2)。 - 表格表头:
<table>缺少<th>(Major,1.3.1)。 - 视频字幕:
<video>缺少<track>(Major,1.2.2)。 - 标题层级跳跃:如 h1 直接跳到 h3(Major,1.3.1/2.4.6)。
- 颜色对比:设置前景/填充色却未就近声明背景,对比度可能不足(Minor,1.4.3)。
颜色对比度(contrast_checker.py)
按 WCAG 相对亮度公式计算对比度,判定 AA(普通文本 4.5:1、大字 3:1)与 AAA(7:1 / 4.5:1)。
# 单对颜色
python scripts/contrast_checker.py --fg "#777777" --bg "#ffffff"
# 失败时给出替代色建议
python scripts/contrast_checker.py --fg "#777777" --bg "#ffffff" --suggest
# 扫描 CSS 文件,按规则块配对检测
python scripts/contrast_checker.py --file styles.css --json
# AAA 级别
python scripts/contrast_checker.py --fg "#595959" --bg "#ffffff" --level aaa
> 实测:#777777 白底对比度 4.48:1,普通文本 AA 不达标;#595959 白底 7.0:1,AA/AAA 双达标。
常见陷阱与正确做法
| 陷阱 | 正确做法 |
|---|---|
<div role="button"> |
用原生 <button>,键盘处理免费获得 |
给所有元素加 tabindex="0" |
仅交互元素需聚焦,优先原生元素 |
在非交互元素上加 aria-label |
用 aria-labelledby 指向可见文本 |
用 display:none 隐藏屏幕阅读器内容 |
改用 .sr-only 类 |
| 仅靠颜色传达含义 | 加图标/文字/图案 |
| 仅用 placeholder 当 label | 始终提供可见 <label> |
outline:none 却不替代 |
用 :focus-visible 提供可见焦点指示 |
信息图 alt="" |
信息图需描述性 alt |
| 标题层级跳跃(h1→h3) | 层级必须连续 |
onClick 无 onKeyDown |
加键盘支持或改用原生元素 |
忽略 prefers-reduced-motion |
动画包在 @media (prefers-reduced-motion: no-preference) 中 |
人工复核清单(静态扫描之外必做)
- 纯键盘走查:Tab/Shift+Tab、Enter/Space、Esc 是否都可用。
- 屏幕阅读器走查:NVDA/VoiceOver 是否能读出结构、链接与表单目的。
- 视觉:放大 200% 不破版;文本对比度达标;焦点指示可见。
- 表单:错误信息与输入关联(
aria-describedby)。
CI 集成示例
- name: a11y-scan
run: python scripts/a11y_scanner.py src --ci
说明
- 两个脚本均仅依赖 Python 标准库,无需安装任何包,可直接在 CI 运行。
- 上游仓库含 380+ 技能,本技能仅取其
a11y-audit单技能做等价改编,不引入任何专有依赖。 - 静态扫描基于源码文本匹配,对模板语法敏感;它用于拦住明显违规与回归,正式合规仍需结合真实辅助技术测试。