a11y-audit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRGAA 4.1.2 Runtime Accessibility Auditor
RGAA 4.1.2 运行时可访问性审计工具
Audit live pages for RGAA 4.1.2 compliance using Playwright and axe-core. Complements (static code) with runtime checks that require a rendered browser environment: real contrast ratios, dynamic content, focus order, and more.
a11y-web使用Playwright和axe-core对在线页面进行RGAA 4.1.2合规性审计。补充(静态代码审计)的功能,提供需要渲染浏览器环境的运行时检查:真实对比度、动态内容、焦点顺序等。
a11y-webWhen to Use This Skill
何时使用该技能
- User provides a sitemap URL (e.g., )
https://site.com/sitemap.xml - User provides a list of URLs to audit
- User wants to audit a deployed site (staging or production)
- User asks to "check a11y on production", "audit live pages", "run RGAA on the site"
For source code analysis, use instead.
a11y-web- 用户提供站点地图URL(例如:)
https://site.com/sitemap.xml - 用户提供待审计的URL列表
- 用户希望审计已部署站点(staging或生产环境)
- 用户提出类似“check a11y on production”、“audit live pages”、“run RGAA on the site”的需求
如需源代码分析,请使用。
a11y-webDependencies
依赖项
This skill requires Playwright and axe-core. Verify they are available before running.
bash
undefined该技能需要Playwright和axe-core。运行前请确认它们已可用。
bash
undefinedCheck Playwright
检查Playwright版本
npx playwright --version
npx playwright --version
Check axe-core (used via CDN injection — no local install needed)
检查axe-core(通过CDN注入使用,无需本地安装)
OR install locally:
或本地安装:
npm install --save-dev axe-core
If Playwright is not installed, run the setup script:
```bash
bash skills/a11y-audit/scripts/setup.shThe setup script installs Playwright and verifies the environment.
See for details.
scripts/setup.shnpm install --save-dev axe-core
如果未安装Playwright,请运行安装脚本:
```bash
bash skills/a11y-audit/scripts/setup.sh该安装脚本会安装Playwright并验证环境。详情请查看。
scripts/setup.shModes
模式
Single URL
单个URL
Audit one page. User provides:
https://example.com/page审计单个页面。用户提供:
https://example.com/pageSitemap Audit (default)
站点地图审计(默认)
Fetch sitemap XML → extract URLs → audit each page in sequence.
User provides:
https://example.com/sitemap.xml获取站点地图XML → 提取URL → 依次审计每个页面。
用户提供:
https://example.com/sitemap.xmlURL List
URL列表
User provides a list of URLs (file or inline).
用户提供URL列表(文件或内联形式)。
Workflow
工作流程
Step 0 — Verify Playwright
步骤0 — 验证Playwright
undefinedundefinedEnsure Playwright browser is available
确保Playwright浏览器可用
mcp__playwright__browser_install (if needed)
If browser install fails, report the error and stop. Do not proceed without a working browser.mcp__playwright__browser_install (如有需要)
如果浏览器安装失败,报告错误并停止。浏览器不可用时请勿继续。Step 1 — Get URL List
步骤1 — 获取URL列表
From sitemap:
WebFetch: GET [sitemap_url]Parse the XML response to extract elements.
<loc>javascript
// Sitemap parsing — extract URLs
const urls = xmlContent.match(/<loc>(.*?)<\/loc>/g)
.map(loc => loc.replace(/<\/?loc>/g, '').trim());For large sitemaps (> 50 URLs): audit the first 20 by default, or the N pages specified by the user. Large sitemaps may have nested sitemaps ( elements) — follow them recursively up to one level.
<sitemap>From URL list: use as-is.
From single URL: wrap in array.
从站点地图获取:
WebFetch: GET [sitemap_url]Parse the XML response to extract elements.
<loc>javascript
// 站点地图解析 — 提取URL
const urls = xmlContent.match(/<loc>(.*?)<\/loc>/g)
.map(loc => loc.replace(/<\/?loc>/g, '').trim());对于大型站点地图(超过50个URL):默认审计前20个页面,或用户指定的N个页面。大型站点地图可能包含嵌套站点地图(元素)—— 递归跟进一级嵌套。
<sitemap>从URL列表获取: 直接使用。
从单个URL获取: 包装为数组。
Step 2 — For Each Page: Navigate and Audit
步骤2 — 对每个页面:导航并审计
For each URL, execute Steps 2a–2e in sequence.
对每个URL,依次执行步骤2a至2e。
2a — Navigate
2a — 导航
browser_navigate: url
browser_wait_for: { state: "load" }If navigation fails (timeout, 404, auth redirect), mark page as and continue.
[SKIP]browser_navigate: url
browser_wait_for: { state: "load" }如果导航失败(超时、404、授权重定向),标记页面为并继续。
[SKIP]2b — Inject and Run axe-core
2b — 注入并运行axe-core
javascript
// browser_evaluate — inject axe-core
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
document.head.appendChild(script);
await new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
setTimeout(reject, 5000);
});javascript
// browser_evaluate — run axe with WCAG 2.1 AA (covers most RGAA criteria)
const results = await axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']
}
});
return {
violations: results.violations,
passes: results.passes.length,
incomplete: results.incomplete.length
};If axe-core CDN fails (e.g., network restrictions), try loading from local node_modules:
javascript
// Fallback — inline axe-core from local install
// (only if npm install axe-core was run)javascript
// browser_evaluate — 注入axe-core
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
document.head.appendChild(script);
await new Promise((resolve, reject) => {
script.onload = resolve;
script.onerror = reject;
setTimeout(reject, 5000);
});javascript
// browser_evaluate — 使用WCAG 2.1 AA标准运行axe(覆盖大多数RGAA准则)
const results = await axe.run(document, {
runOnly: {
type: 'tag',
values: ['wcag2a', 'wcag2aa', 'wcag21a', 'wcag21aa', 'best-practice']
}
});
return {
violations: results.violations,
passes: results.passes.length,
incomplete: results.incomplete.length
};如果axe-core CDN加载失败(例如网络限制),尝试从本地node_modules加载:
javascript
// 备选方案 — 从本地安装的axe-core内联加载
// (仅在运行npm install axe-core后可用)2c — Custom DOM Queries (RGAA gaps in axe-core)
2c — 自定义DOM查询(axe-core未覆盖的RGAA准则)
axe-core does not cover all RGAA criteria. Run these additional checks:
javascript
// browser_evaluate — custom RGAA checks
const customChecks = {
// RGAA 8.3 — lang attribute value
langValue: document.documentElement.lang,
// RGAA 9.1 — heading hierarchy
headings: Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'))
.map(h => ({ level: parseInt(h.tagName[1]), text: h.textContent.trim().slice(0, 60) })),
// RGAA 12.7 — skip link: first focusable element
firstFocusable: (() => {
const el = document.querySelector(
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
return el ? { tag: el.tagName, href: el.getAttribute('href'), text: el.textContent.trim().slice(0, 60) } : null;
})(),
// RGAA 12.6 — landmark presence
landmarks: {
main: !!document.querySelector('main, [role="main"]'),
nav: document.querySelectorAll('nav, [role="navigation"]').length,
header: !!document.querySelector('header, [role="banner"]'),
footer: !!document.querySelector('footer, [role="contentinfo"]')
},
// RGAA 11.1 — inputs without labels
unlabeledInputs: Array.from(
document.querySelectorAll('input:not([type="hidden"]):not([aria-label]):not([aria-labelledby])')
).filter(input => {
const id = input.id;
return !id || !document.querySelector(`label[for="${id}"]`);
}).map(i => ({ type: i.type, name: i.name, id: i.id })),
// RGAA 8.5 — page title
title: document.title,
// RGAA 3.1 — inputs/spans with only color-based class names (heuristic)
colorOnlyIndicators: Array.from(
document.querySelectorAll('[class*="red"],[class*="green"],[class*="error"],[class*="success"]')
).filter(el => !el.textContent.trim()).length,
// RGAA 5.4 — tables without caption
tablesWithoutCaption: Array.from(document.querySelectorAll('table'))
.filter(t => !t.querySelector('caption') && t.getAttribute('role') !== 'presentation')
.length,
// RGAA 1.1 — images without alt
imgsWithoutAlt: Array.from(document.querySelectorAll('img:not([alt])'))
.map(i => i.src.split('/').pop())
};
return customChecks;axe-core未覆盖所有RGAA准则。运行以下额外检查:
javascript
// browser_evaluate — 自定义RGAA检查
const customChecks = {
// RGAA 8.3 — lang属性值
langValue: document.documentElement.lang,
// RGAA 9.1 — 标题层级
headings: Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'))
.map(h => ({ level: parseInt(h.tagName[1]), text: h.textContent.trim().slice(0, 60) })),
// RGAA 12.7 — 跳过链接:第一个可聚焦元素
firstFocusable: (() => {
const el = document.querySelector(
'a[href], button, input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
return el ? { tag: el.tagName, href: el.getAttribute('href'), text: el.textContent.trim().slice(0, 60) } : null;
})(),
// RGAA 12.6 — 地标元素存在性
landmarks: {
main: !!document.querySelector('main, [role="main"]'),
nav: document.querySelectorAll('nav, [role="navigation"]').length,
header: !!document.querySelector('header, [role="banner"]'),
footer: !!document.querySelector('footer, [role="contentinfo"]')
},
// RGAA 11.1 — 无标签的输入框
unlabeledInputs: Array.from(
document.querySelectorAll('input:not([type="hidden"]):not([aria-label]):not([aria-labelledby])')
).filter(input => {
const id = input.id;
return !id || !document.querySelector(`label[for="${id}"]`);
}).map(i => ({ type: i.type, name: i.name, id: i.id })),
// RGAA 8.5 — 页面标题
title: document.title,
// RGAA 3.1 — 仅含颜色类名的输入框/span(启发式检查)
colorOnlyIndicators: Array.from(
document.querySelectorAll('[class*="red"],[class*="green"],[class*="error"],[class*="success"]')
).filter(el => !el.textContent.trim()).length,
// RGAA 5.4 — 无标题的表格
tablesWithoutCaption: Array.from(document.querySelectorAll('table'))
.filter(t => !t.querySelector('caption') && t.getAttribute('role') !== 'presentation')
.length,
// RGAA 1.1 — 无alt属性的图片
imgsWithoutAlt: Array.from(document.querySelectorAll('img:not([alt])'))
.map(i => i.src.split('/').pop())
};
return customChecks;2d — Screenshot
2d — 截图
browser_take_screenshot: { fullPage: false }Save for reference in the report (optional, activated by flag).
--screenshotsbrowser_take_screenshot: { fullPage: false }保存截图供报告参考(可选,通过 flag激活)。
--screenshots2e — Get Accessibility Tree
2e — 获取可访问性树
browser_snapshotUse to verify landmark structure and heading hierarchy in the rendered tree.
browser_snapshot用于验证渲染树中的地标结构和标题层级。
Step 3 — Build Per-Page Report
步骤3 — 构建单页报告
For each page, produce a structured section:
markdown
undefined对每个页面,生成结构化报告章节:
markdown
undefinedPage: [URL]
页面: [URL]
Status: 200 OK | Audited at: YYYY-MM-DD HH:MM
状态: 200 OK | 审计时间: YYYY-MM-DD HH:MM
axe-core Violations
axe-core 违规项
| RGAA | axe Rule | Severity | Element | Message |
|---|---|---|---|---|
| 3.2 | color-contrast | critical | | Contrast ratio 2.8:1 (min 4.5:1) |
| 1.1 | image-alt | critical | | Missing alt attribute |
| 12.7 | skip-link | moderate | — | No skip navigation link found |
| RGAA | axe规则 | 严重程度 | 元素 | 消息 |
|---|---|---|---|---|
| 3.2 | color-contrast | critical | | 对比度2.8:1(最低要求4.5:1) |
| 1.1 | image-alt | critical | | 缺少alt属性 |
| 12.7 | skip-link | moderate | — | 未找到跳过导航链接 |
Custom RGAA Checks
自定义RGAA检查
- [PASS] RGAA 8.3 — lang="fr" ✓
- [FAIL] RGAA 9.1 — Heading hierarchy: h1 → h3 (h2 missing)
- [FAIL] RGAA 12.6 — No <main> landmark found
- [PASS] RGAA 12.7 — Skip link present: "Aller au contenu" → #main
- [FAIL] RGAA 11.1 — 2 unlabeled inputs: input[type=email], input[type=search]
- [通过] RGAA 8.3 — lang="fr" ✓
- [失败] RGAA 9.1 — 标题层级: h1 → h3(缺少h2)
- [失败] RGAA 12.6 — 未找到<main>地标元素
- [通过] RGAA 12.7 — 存在跳过链接: "Aller au contenu" → #main
- [失败] RGAA 11.1 — 2个无标签输入框: input[type=email], input[type=search]
Summary
摘要
- axe-core violations: X (Y critical, Z serious, W moderate)
- Custom RGAA failures: N
- axe-core passes: P checks passed
- Incomplete (needs manual review): I
Severity mapping (axe-core → RGAA):
- `critical` → `[FAIL]`
- `serious` → `[FAIL]`
- `moderate` → `[WARN]`
- `minor` → `[WARN]`- axe-core违规项: X(Y个critical,Z个serious,W个moderate)
- 自定义RGAA检查失败: N
- axe-core通过项: P项检查通过
- 待手动审核: I
严重程度映射(axe-core → RGAA):
- `critical` → `[失败]`
- `serious` → `[失败]`
- `moderate` → `[警告]`
- `minor` → `[警告]`Step 4 — Global Summary Report
步骤4 — 全局摘要报告
After all pages are audited, produce a consolidated report:
markdown
undefined完成所有页面审计后,生成综合报告:
markdown
undefinedRGAA 4.1.2 Accessibility Audit Report
RGAA 4.1.2 可访问性审计报告
Site: [base URL]
Sitemap: [sitemap URL]
Date: YYYY-MM-DD
Pages audited: N / Total in sitemap
站点: [基础URL]
站点地图: [站点地图URL]
日期: YYYY-MM-DD
已审计页面数: N / 站点地图总页面数
Critical Issues (across all pages)
严重问题(所有页面)
| RGAA | Issue | Occurrences | Pages affected |
|---|---|---|---|
| 3.2 | Insufficient contrast | 47 | 12/20 pages |
| 1.1 | Missing alt on images | 23 | 8/20 pages |
| 12.6 | Missing <main> landmark | 3 | 2/20 pages |
| RGAA | 问题 | 出现次数 | 受影响页面数 |
|---|---|---|---|
| 3.2 | 对比度不足 | 47 | 12/20页面 |
| 1.1 | 图片缺少alt属性 | 23 | 8/20页面 |
| 12.6 | 缺少<main>地标元素 | 3 | 2/20页面 |
Per-Page Summary
单页摘要
| Page | Violations | Critical | Warnings | Status |
|---|---|---|---|---|
| /home | 12 | 3 | 9 | ⚠ |
| /about | 0 | 0 | 0 | ✓ |
| /contact | 5 | 2 | 3 | ⚠ |
| 页面 | 违规项 | 严重 | 警告 | 状态 |
|---|---|---|---|---|
| /home | 12 | 3 | 9 | ⚠ |
| /about | 0 | 0 | 0 | ✓ |
| /contact | 5 | 2 | 3 | ⚠ |
Compliance Estimate
合规性估算
Estimated RGAA compliance rate: XX% (based on auditable criteria)
RGAA合规率估算: XX%(基于可审计准则)
Recommended Priority Actions
推荐优先操作
- Fix contrast issues (affects X% of users, critical severity, XX occurrences)
- Add alt text to images (XX occurrences on X pages)
- Add <main> landmark to layout template (affects all pages)
- 修复对比度问题(影响X%用户,严重级别,XX次出现)
- 为图片添加alt文本(X个页面出现XX次)
- 为布局模板添加<main>地标元素(影响所有页面)
Limitations
局限性
- Interactive content (modals, tooltips, carousels) requires manual testing
- Authentication-protected pages were skipped
- Dynamic content loaded after interaction not audited
- Recommended: complement with manual screen reader testing (NVDA/JAWS/VoiceOver)
Save the report to a file:Write: a11y-audit-report-YYYY-MM-DD.md
---- 交互式内容(模态框、提示框、轮播)需要手动测试
- 受身份验证保护的页面已跳过
- 交互后加载的动态内容未被审计
- 建议:结合手动屏幕阅读器测试(NVDA/JAWS/VoiceOver)
将报告保存到文件:Write: a11y-audit-report-YYYY-MM-DD.md
---axe-core → RGAA Mapping
axe-core → RGAA 映射表
| RGAA Criterion | axe-core Rule(s) |
|---|---|
| 1.1 — Image alt | |
| 1.2 — Decorative images | |
| 3.2 — Contrast | |
| 6.1 — Link names | |
| 6.2 — Image links | |
| 8.3 — HTML lang | |
| 8.4 — Lang valid | |
| 8.5 — Page title | |
| 9.1 — Headings | |
| 11.1 — Form labels | |
| 11.2 — Placeholder | |
| 11.5 — Fieldset | |
| 12.1 — Multiple nav | — (custom check) |
| 12.6 — Landmarks | |
| 12.7 — Skip link | |
| 12.8 — Skip link functional | |
Criteria not covered by axe-core (custom checks handle these):
- RGAA 3.1 — Color-only information (heuristic only)
- RGAA 9.1 — Heading level skip detection
- RGAA 12.6 — Multiple nav without aria-label
| RGAA准则 | axe-core规则 |
|---|---|
| 1.1 — 图片alt | |
| 1.2 — 装饰性图片 | |
| 3.2 — 对比度 | |
| 6.1 — 链接名称 | |
| 6.2 — 图片链接 | |
| 8.3 — HTML lang属性 | |
| 8.4 — lang属性有效性 | |
| 8.5 — 页面标题 | |
| 9.1 — 标题 | |
| 11.1 — 表单标签 | |
| 11.2 — 占位符 | |
| 11.5 — Fieldset | |
| 12.1 — 多个导航 | —(自定义检查) |
| 12.6 — 地标元素 | |
| 12.7 — 跳过链接 | |
| 12.8 — 跳过链接功能 | |
axe-core未覆盖的准则(由自定义检查处理):
- RGAA 3.1 — 仅含颜色信息(仅启发式检查)
- RGAA 9.1 — 标题层级跳跃检测
- RGAA 12.6 — 无aria-label的多个导航
Limitations
局限性
| What cannot be audited automatically | Why |
|---|---|
| Authentication-protected pages | Requires login — provide session cookies if needed |
| Dynamic content (modals, live regions) | Requires interaction to trigger |
| PDF / Office documents | Not browser-renderable |
| Video captions (RGAA topic 4) | Requires human review |
| Touch target sizes | Requires device-specific testing |
| Screen reader announcements | Requires real AT (NVDA, JAWS, VoiceOver) |
For thorough RGAA compliance, complement this audit with:
- — static source code analysis
a11y-web - Manual screen reader walkthrough (NVDA/Firefox, VoiceOver/Safari)
- Keyboard-only navigation test
| 无法自动审计的内容 | 原因 |
|---|---|
| 受身份验证保护的页面 | 需要登录 — 如有需要请提供会话cookie |
| 动态内容(模态框、实时区域) | 需要交互触发 |
| PDF / Office文档 | 无法在浏览器中渲染 |
| 视频字幕(RGAA第4主题) | 需要人工审核 |
| 触摸目标尺寸 | 需要特定设备测试 |
| 屏幕阅读器播报 | 需要真实辅助技术(NVDA, JAWS, VoiceOver) |
如需全面的RGAA合规性,建议结合以下审计方式:
- — 静态源代码分析
a11y-web - 手动屏幕阅读器遍历测试(NVDA/Firefox, VoiceOver/Safari)
- 纯键盘导航测试
Reference
参考链接
Official RGAA 4.1.2: https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/
axe-core rules: https://dequeuniversity.com/rules/axe/
Playwright docs: https://playwright.dev/docs/api/class-page
官方RGAA 4.1.2: https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/
axe-core规则: https://dequeuniversity.com/rules/axe/
Playwright文档: https://playwright.dev/docs/api/class-page