mcpField
A
audit-accessibility

audit-accessibility

github.com/kensaurusテスト・品質940 回インストール1 週間前に更新
リポジトリ

audit-accessibility について

audit-accessibilityは、WebプロジェクトをWCAG 2.2 AAレベルで監査するスキルです。自動スキャン、キーボードや支援技術の確認、静的コード分析を組み合わせ、根拠に基づく報告を支援します。

主な機能

  1. 01ルートと既存ツールの検出
  2. 02Playwrightによるaxe-coreスキャン
  3. 03WCAGとキーボード操作の手動確認
  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 は [HIGH freedom]、フェーズ 2~3 の playwright/axe/Tab のシーケンスは [LOW freedom — run exactly] です。ブラウザーを使う前に protocol-browser-anti-stall を読んでください。

推論方法

  1. 観察 — axe のルールとセレクター、見出し一覧、または Tab のスナップショットを引用します
  2. 解釈 — キーボードや支援技術のユーザーがタスクを完了できますか?
  3. 分類 — WCAG の不適合/axe の誤検知/手動のみ/現状で適切
  4. 重大度 — レベル A = P0、レベル AA = P1、ベストプラクティス = P2

実例

観察: /signup で axe の label。<input name="email"> に id/aria-label がありません。 解釈: 支援技術のユーザーは何を入力すべきかわかりません(1.3.1/4.1.2)。 分類: WCAG の不適合(axe の誤検知ではありません)。 重大度: P0 — レベル A、サインアップを妨げます。 指摘: /signup | 1.3.1+4.1.2 | P0 | ラベルのないメールアドレス | <label for>。

報告前の自己批評 [LOW freedom — do not skip]

  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)
  • 影響度(critical、serious、moderate、minor)
  • ルールに対応する 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 は 1 つだけであること、見出しのレベルを飛ばしていないこと(h2 を使わず h1 -> h3 など)。

画像の代替テキスト(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. 状態変更後のフォーカス管理

動的な更新後にフォーカスが適切に管理されることをテストします。

  • モーダルを開いた後:フォーカスがモーダル内に移動する
  • モーダルを閉じた後:フォーカスがトリガーに戻る
  • リストから項目を削除した後:フォーカスが次の項目または論理的な位置に移動する
  • フォームの送信後:フォーカスが成功/エラーメッセージに移動する

フェーズ 4: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"
 }
}

フェーズ 5:静的コード解析

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}")

白い背景に薄い灰色のテキストを配置するケースは、コントラスト不足の最も一般的な原因です。薄い灰色のテキストカラーを使用しているファイルにフラグを付け、手動でコントラストを確認します。


フェーズ 6:レポート

═══════════════════════════════════════════════════════
 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 | ... |

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

コメント