mcpField
A
audit-accessibility

audit-accessibility

github.com/kensaurus测试与质量940 次安装1 周前更新
代码仓库

关于 audit-accessibility

audit-accessibility 是面向 Web 项目的 WCAG 2.2 AA 级审计技能,适用于开发者和审查者。它结合自动扫描、键盘与辅助技术检查、静态代码分析及证据化报告。

主要功能

  1. 01发现路由与现有工具
  2. 02使用 Playwright 运行 axe-core 扫描
  3. 03手动检查 WCAG 与键盘操作
  4. 04静态分析无障碍代码
  5. 05按严重性生成审计报告

适用场景

  1. 01检查页面的 WCAG 违规项
  2. 02测试键盘导航与焦点管理
  3. 03审查表单标签和图片替代文本
  4. 04整理并报告分级问题

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/kensaurus/skills --skill audit-accessibility

安装后在 Claude Code 中输入/audit-accessibility确认已加载。

详细介绍

无障碍审计

自由度:混合 — 阶段 0–1、5 [自由度高];阶段 2–3 中的 Playwright/axe/Tab 序列 [自由度低 — 严格按步骤执行]。执行任何浏览器操作前,请先阅读 protocol-browser-anti-stall。

如何分析

  1. 观察 — 引用 axe 规则和选择器、标题列表或 Tab 快照
  2. 解读 — 键盘用户或 AT 用户能否完成任务?
  3. 分类 — WCAG 不符合 / axe 误报 / 仅需手动检查 / 正确无误
  4. 严重程度 — A 级 = P0;AA 级 = P1;最佳实践 = P2

示例

观察: /signup 上的 axe label 规则;<input name="email"> 没有 id/aria-label。 解读: AT 用户无法判断应输入什么(1.3.1 / 4.1.2)。 分类: WCAG 不符合(并非 axe 误报)。 严重程度: P0 — A 级,阻碍注册。 问题: /signup | 1.3.1+4.1.2 | P0 | 邮箱字段未标注 | <label for>。

报告前自我检查 [自由度低 — 不要跳过]

  1. WCAG 成功标准 + 编号 — 不要只说“对比度不好”;引用比例或选择器
  2. 检查键盘,不要只依赖 axe — 即使 axe 未发现问题,2.1.1 仍可能不符合
  3. 检查所有可访问的页面 — 冷门路由也要检查
  4. 说明严重程度的依据 — A 级 = P0
  5. axe 覆盖率约为 30–40% — 标题、焦点、链接文本和阅读顺序始终需要手动检查

阶段 0:自动检测页面和组件

0a. 探索路由结构
Glob("**/app/**/page.{tsx,jsx,ts,js}")
Glob("**/pages/**/*.{tsx,jsx,ts,js}")
Glob("**/src/routes/**/*.{tsx,jsx,svelte,vue}")
0b. 查找应用 URL
Grep(pattern: "NEXT_PUBLIC_APP_URL|NEXT_PUBLIC_BASE_URL|VITE_APP_URL|APP_URL|localhost", glob: ".env*")

如果没有生产环境 URL,则默认使用 http://localhost:3000(或检测到的开发服务器端口)。

0c. 检测 CSS 框架和组件库
Grep(pattern: "tailwindcss|@chakra-ui|@mui|@radix-ui|shadcn|bootstrap|bulma", glob: "package.json")
0d. 检测现有 a11y 工具
Grep(pattern: "axe-core|@axe-core|eslint-plugin-jsx-a11y|pa11y|lighthouse|@testing-library", glob: "package.json")
Grep(pattern: "aria-|role=|tabIndex|sr-only|visually-hidden", glob: "*.{tsx,jsx,vue,svelte}", output_mode: "count")

记录:APP_URL、PAGES、CSS_FRAMEWORK、COMPONENT_LIB、EXISTING_A11Y_TOOLS、ARIA_USAGE_COUNT。


阶段 1:研究当前 WCAG 指南

1a. Firecrawl 研究
firecrawl:firecrawl_search
{
 "query": "WCAG 2.2 success criteria checklist web accessibility [current year]",
 "limit": 5
}
firecrawl:firecrawl_search
{
 "query": "axe-core automated accessibility testing best practices common false positives",
 "limit": 5
}
firecrawl:firecrawl_search
{
 "query": "<CSS_FRAMEWORK> <COMPONENT_LIB> accessibility best practices common issues",
 "limit": 5
}

抓取 2–3 个权威结果:

firecrawl:firecrawl_scrape
{
 "url": "<BEST_RESULT_URL>",
 "formats": ["markdown"],
 "onlyMainContent": true
}
1b. 特定框架的 a11y 文档(Context7)
context7:resolve-library-id
{
 "libraryName": "<COMPONENT_LIB e.g. radix-ui or chakra-ui>"
}
context7:query-docs
{
 "libraryId": "<RESOLVED_ID>",
 "query": "accessibility ARIA keyboard navigation focus management"
}

阶段 2:自动扫描(通过 Playwright 使用 axe-core)

2a. 导航至每个页面

对于阶段 0a 中发现的每个页面:

PW="npx --yes @playwright/cli@latest"
$PW -s=a11y open --headed "<APP_URL><ROUTE>"    # first page; use `goto` for subsequent ones

应用 protocol-browser-anti-stall 协议:等待 2 秒,获取快照,并确认页面已加载。

2b. 注入并运行 axe-core

使用 run-code 从 CDN 注入 axe-core 并执行完整扫描。URL 固定了版本,确保结果可复现;如果 CDN 返回 404,请在 cdnjs 上查找当前的 axe-core 4.x 版本并替换 — 不要跳过扫描:

$PW -s=a11y run-code 'async (page) => { await page.addScriptTag({ url: "https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js" }); return await page.evaluate(async () => { const r = await axe.run(); return { violations: r.violations.map(v => ({ id: v.id, impact: v.impact, description: v.description, help: v.help, helpUrl: v.helpUrl, nodes: v.nodes.length, targets: v.nodes.slice(0, 3).map(n => n.target[0]) })), passes: r.passes.length, incomplete: r.incomplete.length, inapplicable: r.inapplicable.length }; }); }'

对于返回的每项违规,记录:

  • 规则 ID(例如 color-contrast、image-alt、label)
  • 影响程度(严重、较高、中等、轻微)
  • 规则对应的 WCAG 标准
  • 受影响元素(CSS 选择器)
  • 受影响节点数
2c. 手动检查特定 WCAG 类别

标题层级(WCAG 1.3.1):

$PW -s=a11y eval '() => Array.from(document.querySelectorAll("h1,h2,h3,h4,h5,h6")).map(h => ({ tag: h.tagName, text: h.textContent.trim().substring(0, 50) }))'

验证:只有一个 h1,标题层级不跳级(例如 h1 后直接出现 h3,中间没有 h2)。

图片替代文本(WCAG 1.1.1):

$PW -s=a11y eval '() => Array.from(document.querySelectorAll("img")).map(img => ({ src: img.src.split("/").pop(), alt: img.alt, hasAlt: img.hasAttribute("alt"), decorative: img.getAttribute("role") === "presentation" || img.alt === "" }))'

验证:所有信息性图片都有描述性替代文本;装饰性图片使用 alt="" 或 role="presentation"。

链接文本(WCAG 2.4.4):

$PW -s=a11y eval '() => Array.from(document.querySelectorAll("a")).filter(a => ["click here", "here", "read more", "learn more", "", "link"].includes((a.textContent || "").trim().toLowerCase())).map(a => ({ href: a.href, text: (a.textContent || "").trim(), ariaLabel: a.getAttribute("aria-label") }))'

标记通用链接文本(“点击此处”、“阅读更多”以及没有 aria-label 的空链接)。

表单标签(WCAG 1.3.1、4.1.2):

$PW -s=a11y eval '() => Array.from(document.querySelectorAll("input,select,textarea")).map(el => ({ type: el.type, name: el.name, id: el.id, hasLabel: !!el.id && !!document.querySelector(`label[for="${el.id}"]`), ariaLabel: el.getAttribute("aria-label"), ariaLabelledBy: el.getAttribute("aria-labelledby"), placeholder: el.placeholder }))'

验证:每个表单控件都关联了 <label>、aria-label 或 aria-labelledby。

语言属性(WCAG 3.1.1):

$PW -s=a11y eval '() => ({ htmlLang: document.documentElement.lang, htmlDir: document.documentElement.dir })'

验证:<html> 具有有效的 lang 属性。

2d. 截取证据屏幕截图

为每个页面截取屏幕截图作为视觉证据:

$PW -s=a11y screenshot --filename ".playwright-mcp/a11y-<route>.png"

阶段 3:键盘导航测试

3a. Tab 顺序测试

对于每个页面,测试所有交互元素是否都可以通过 Tab 键访问:

PW="npx --yes @playwright/cli@latest"
$PW -s=a11y open --headed "<APP_URL><ROUTE>"    # first page; use `goto` for subsequent ones

反复按 Tab,并在每次按键后截取快照,以跟踪焦点移动:

$PW -s=a11y press Tab
$PW -s=a11y snapshot

每次按 Tab 后,检查:

  • 焦点清晰可见(当前元素上有焦点指示器)
  • 焦点顺序合理(符合视觉阅读顺序)
  • 没有焦点陷阱(Tab 最终会循环回到起点或到达终点)
  • 存在跳过链接(页面有较大的导航区域时)

持续按 Tab,直到焦点访问过页面上的每个交互元素或循环回到第一个元素;只有发现焦点陷阱时才提前停止,并记录下来。

3b. 交互元素测试

对于每个交互组件(下拉菜单、模态框、选项卡、折叠面板):

Enter/Space 激活(WCAG 2.1.1):

  • 按 Tab 将焦点移至该元素
  • 按 Enter 或 Space
  • 确认操作已触发(截取快照以确认状态变化)

按 Escape 关闭(适用于模态框/对话框的 WCAG 2.1.1):

$PW -s=a11y press Escape

验证:模态框/下拉菜单会关闭,焦点会返回触发元素。

方向键导航(适用于选项卡面板、菜单、列表框):

$PW -s=a11y press ArrowDown
3c. 状态变化后的焦点管理

测试动态更新后焦点是否得到正确管理:

  • 打开模态框后:焦点应移入模态框
  • 关闭模态框后:焦点应返回触发元素
  • 从列表中删除项目后:焦点应移至下一项或合理的位置
  • 表单提交后:焦点应移至成功/错误消息

第四阶段:与 Sentry 交叉核查

4a. 搜索辅助技术错误
sentry:search_issues
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlugOrId": "<PROJECT_SLUG>",
 "query": "is:unresolved aria OR screenreader OR voiceover OR talkback OR nvda OR jaws OR focus OR tabindex OR keyboard"
}
4b. 检查键盘交互时的 JavaScript 错误
sentry:search_issues
{
 "organizationSlug": "<ORG_SLUG>",
 "projectSlugOrId": "<PROJECT_SLUG>",
 "query": "is:unresolved keydown OR keyup OR keypress OR focusin OR focusout"
}
4c. 浏览器/设备细分

如果错误集中出现在辅助技术用户偏好的浏览器中,请调取该问题的浏览器分布数据。get_issue_tag_values 是一个目录工具,而非直接工具,因此请通过 execute_sentry_tool 调用它。如果调用被拒绝,请运行 search_sentry_tools 并使用 "issue tag values" 获取当前名称和架构。

sentry:execute_sentry_tool
{
 "name": "get_issue_tag_values",
 "arguments": {
  "organizationSlug": "<ORG_SLUG>",
  "issueId": "<ISSUE_ID>",
  "tagKey": "browser"
 }
}

第五阶段:静态代码分析

5a. 缺少 ARIA 属性
Grep(pattern: "onClick(?!.*role=)(?!.*tabIndex)", glob: "*.{tsx,jsx}")

标记带有点击处理程序却没有 role 或 tabIndex 的 div 或 span 元素——键盘和屏幕阅读器用户无法感知这些元素。

5b. 无法访问的自定义组件
SemanticSearch(query: "Where are custom interactive components defined (dropdowns, modals, tabs, accordions) that don't use a component library?", target_directories: [])

检查每个自定义交互元素是否具备:

  • 正确的 ARIA 角色(role="dialog"、role="tabpanel" 等)
  • 键盘事件处理程序(onKeyDown)
  • 焦点管理(useRef + .focus())
  • ARIA 状态(aria-expanded、aria-selected、aria-hidden)
5c. CSS/Tailwind 中的颜色对比度
Grep(pattern: "text-gray-[345]|text-slate-[345]|text-zinc-[345]|text-neutral-[345]|color:\\s*#[789abc]", glob: "*.{tsx,jsx,css,scss}")

白色背景上的浅灰色文本是最常见的对比度问题。标记使用浅灰色文本颜色的文件,以便手动检查对比度。


第六阶段:报告

═══════════════════════════════════════════════════════
 WCAG 2.2 ACCESSIBILITY AUDIT REPORT
 Project: <PROJECT_NAME>
 Date: <DATE>
 Standard: WCAG 2.2 Level AA
 Pages Tested: <COUNT>
═══════════════════════════════════════════════════════

## 1. EXECUTIVE SUMMARY

| Metric | Result |
|---------------------------|-----------------|
| Pages tested | X |
| Total violations | N |
| Critical violations | N |
| Serious violations | N |
| Level A criteria failed / checked | N / M |
| Level AA criteria failed / checked | N / M |
| axe-core rules passed | N |
| Keyboard navigable pages | X/Y |

## 2. AXE-CORE SCAN RESULTS (per page)

| Page | Critical | Serious | Moderate | Minor | Pass | Worst level failed (A / AA / none) |
|------|----------|---------|----------|-------|------|------|
| / | ... | ... | ... | ... | ... | A / AA / none |
| /about | ... | ... | ... | ... | ... | A / AA / none |

## 3. VIOLATIONS BY WCAG CRITERION

| WCAG SC | Name | Level | Pages Affected | Elements | Impact | Status |
|---------|------|-------|----------------|----------|--------|--------|
| 1.1.1 | Non-text Content | A | ... | ... | ... | PASS/FAIL |
| 1.3.1 | Info and Relationships | A | ... | ... | ... | PASS/FAIL |
| 1.4.3 | Contrast (Minimum) | AA | ... | ... | ... | PASS/FAIL |
| 2.1.1 | Keyboard | A | ... | ... | ... | PASS/FAIL |
| 2.4.3 | Focus Order | A | ... | ... | ... | PASS/FAIL |
| 2.4.4 | Link Purpose | A | ... | ... | ... | PASS/FAIL |
| 2.4.7 | Focus Visible | AA | ... | ... | ... | PASS/FAIL |
| 3.1.1 | Language of Page | A | ... | ... | ... | PASS/FAIL |
| 4.1.2 | Name, Role, Value | A | ... | ... | ... | PASS/FAIL |

## 4. KEYBOARD NAVIGATION

| Page | Tab Order Logical | Focus Visible | No Focus Trap | Skip Link | Worst level failed |
|------|-------------------|---------------|---------------|-----------|-------|
| / | ... | ... | ... | ... | A / AA / none |

## 5. INTERACTIVE COMPONENTS

| Component | Location | ARIA Roles | Keyboard Operable | Focus Managed | Worst level failed |
|-----------|----------|------------|-------------------|---------------|-------|
| Modal | ... | ... | ... | ... | A / AA / none |
| Dropdown | ... | ... | ... | ... | A / AA / none |

## 6. SENTRY ASSISTIVE TECHNOLOGY ERRORS

| Issue | Error Type | Events | Browser/AT | Fix Needed |
|-------|------------|--------|------------|------------|
| ... | ... | ... | ... | ... |

## 7. STATIC CODE FINDINGS

| Pattern | Files | Count | Risk |
|---------|-------|-------|------|
| onClick without role/tabIndex | ... | N | High |
| Custom interactive without ARIA | ... | N | High |
| Low-contrast text classes | ... | N | Medium |

## 8. CRITICAL FINDINGS (Action Required)

P0 — Level A violations (legal risk):
1. [finding with WCAG SC, element, page, evidence]

P1 — Level AA violations (compliance gap):
1. [finding with WCAG SC, element, page, evidence]

P2 — Best practice improvements:
1. [finding with rationale]

## 9. RECOMMENDATIONS

| # | WCAG SC | Current | Recommended | Effort | Files to Change |
|---|---------|---------|-------------|--------|-----------------|
| 1 | ... | ... | ... | S/M/L | ... |

═══════════════════════════════════════════════════════

评论

支持 Markdown