a11y-playwright-testing について
Playwright、TypeScript、axe-core を使うチーム向けのアクセシビリティテストスキルです。WCAG 2.2 AA スキャンやキーボード、フォーカス、セマンティクスの検証を支援します。完全な適合確認には手動テストも必要です。
主な機能
- 01axe-core による WCAG 2.2 AA スキャン
- 02キーボード操作とフォーカスのテスト
- 03セマンティクスと ARIA の検証
- 04フォームラベルとエラーの確認
- 05コントラストとスクリーンリーダーのテストパターン
活用シーン
- 01ページやコンポーネントの WCAG 違反確認
- 02ダイアログのフォーカストラップと復帰の検証
- 03キーボード操作とフォームラベルのテスト
- 04アクセシビリティ回帰テストに自動チェックを追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ fugazi/ test-automation-skills-agents --skill a11y-playwright-testingインストール後、Claude Code で/a11y-playwright-testingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
LICENSE.txtreferences/aria-patterns-mistakes.mdaria-patterns-widgets-1.mdaria-patterns-widgets-2.mdaxe-tags-reference.mdsnippets-keyboard-focus-semantic.mdsnippets-setup-and-scanning.mdsnippets-visual-names-checklist.mdwcag21aa-checklist.md詳細
Playwright アクセシビリティテスト(TypeScript)
TypeScript と axe-core を使用した Playwright による、自動アクセシビリティテストの包括的なツールキットです。WCAG 2.2 レベル AA(2.1 を包含)の適合性確認、キーボード操作のテスト、セマンティクスの検証、アクセシビリティのリグレッション防止を実現します。
有効化: アクセシビリティテスト、WCAG 準拠、axe-core スキャン、キーボードナビゲーションテスト、フォーカス管理、ARIA の検証、スクリーンリーダーとの互換性に取り組むときに、このスキルが起動します。
このスキルを使用する場面
- axe-core を使用した、WCAG 2.2 AA 準拠の自動 a11y スキャン
- Tab/Enter/Space/Escape/矢印キーの操作性を確認するキーボードナビゲーションテスト
- ダイアログ、メニュー、動的コンテンツのフォーカス管理の検証
- ランドマーク、見出し、ARIA のセマンティック構造のアサーション
- ラベル、エラー、説明を対象とするフォームアクセシビリティテスト
- 色のコントラストと視覚的アクセシビリティの検証
- スクリーンリーダー互換性テストのパターン
使用しない場面
- Selenium/Java によるアクセシビリティテスト(
accessibility-selenium-testingを使用してください)。 - Playwright による機能/UI の E2E テスト仕様の作成(
playwright-e2e-testingを使用してください)。 - 適合性の完全な最終確認 — 自動 axe スキャンで検出できる問題は約 30~40%です。手動監査と支援技術によるテストも引き続き必要です。
前提条件
| 要件 | 詳細 |
|---|---|
| Node.js | v18+ を推奨 |
| Playwright | @playwright/test のインストール済み |
| axe-core | @axe-core/playwright パッケージ |
| TypeScript | プロジェクトで設定済み |
クイックセットアップ
# Add axe-core to existing Playwright project
npm install -D @axe-core/playwright axe-core
最初に確認すること
アクセシビリティテストを書く前に、次の点を確認してください。
- 対象範囲:対象となるページ/フローはどれですか?明示的に除外するものはありますか?
- 規格:WCAG 2.2 AA(デフォルト)ですか、それとも組織固有のポリシーですか?
- 優先度:最もリスクの高いコンポーネントはどれですか(フォーム、モーダル、ナビゲーション、チェックアウトなど)?
- 例外:既知の制約はありますか(レガシーなマークアップ、サードパーティー製ウィジェットなど)?
- 支援技術:手動テストが必要なスクリーンリーダー/ブラウザーはどれですか?
基本原則
1. 自動化の限界
[!] 重要: 自動ツールで検出できるアクセシビリティの問題は約 30~40%です。自動化はリグレッションの防止と一般的な不具合の検出に利用してください。WCAG への完全な適合には、手動監査が必須です。
2. セマンティック HTML を優先
ARIA よりもネイティブ HTML のセマンティクスを優先してください。必要なセマンティクスをネイティブ要素で実現できない場合にのみ、ARIA を使用してください。
// [ok] Semantic HTML - inherently accessible
await page.getByRole("button", { name: "Submit" }).click();
// [no] ARIA override - requires manual keyboard/focus handling
await page.locator('[role="button"]').click(); // Often a <div>
3. ロケーター戦略をアクセシビリティの指標にする
要素をロールまたはラベルで特定できない場合は、アクセシビリティ上の問題であることがよくあります。
| ロケーターの結果 | アクセシビリティの指標 |
|---|---|
getByRole('button', { name: 'Submit' }) [ok] | ボタンにアクセシブルな名前がある |
getByLabel('Email') [ok] | 入力に適切なラベルが付いている |
getByRole('navigation') [ok] | ランドマークが存在する |
locator('.submit-btn') [!] | アクセシブルな名前がない可能性がある |
主なワークフロー
axe による自動スキャン(WCAG 2.2 AA)
import AxeBuilder from "@axe-core/playwright";
import { test, expect } from "@playwright/test";
test("page has no WCAG 2.2 AA violations", async ({ page }) => {
await page.goto("/");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
.analyze();
expect(results.violations).toEqual([]);
});
範囲を限定した axe スキャン(コンポーネント単位)
test("form component is accessible", async ({ page }) => {
await page.goto("/contact");
const results = await new AxeBuilder({ page })
.include("#contact-form") // Scope to specific component
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
.analyze();
expect(results.violations).toEqual([]);
});
キーボードナビゲーションテスト
test("form is keyboard navigable", async ({ page }) => {
await page.goto("/login");
// Tab to first field
await page.keyboard.press("Tab");
await expect(page.getByLabel("Email")).toBeFocused();
// Tab to password
await page.keyboard.press("Tab");
await expect(page.getByLabel("Password")).toBeFocused();
// Tab to submit button
await page.keyboard.press("Tab");
await expect(page.getByRole("button", { name: "Sign in" })).toBeFocused();
// Submit with Enter
await page.keyboard.press("Enter");
await expect(page).toHaveURL(/dashboard/);
});
ダイアログのフォーカス管理
test("dialog traps and returns focus", async ({ page }) => {
await page.goto("/settings");
const trigger = page.getByRole("button", { name: "Delete account" });
// Open dialog
await trigger.click();
const dialog = page.getByRole("dialog");
await expect(dialog).toBeVisible();
// Focus should be inside dialog
await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();
// Tab should stay trapped in dialog
await page.keyboard.press("Tab");
await expect(dialog.getByRole("button", { name: "Confirm" })).toBeFocused();
await page.keyboard.press("Tab");
await expect(dialog.getByRole("button", { name: "Cancel" })).toBeFocused();
// Escape closes and returns focus to trigger
await page.keyboard.press("Escape");
await expect(dialog).toBeHidden();
await expect(trigger).toBeFocused();
});
スキップリンクの検証
test("skip link moves focus to main content", async ({ page }) => {
await page.goto("/");
// First Tab should focus skip link
await page.keyboard.press("Tab");
const skipLink = page.getByRole("link", { name: /skip to (main|content)/i });
await expect(skipLink).toBeFocused();
// Activating skip link moves focus to main
await page.keyboard.press("Enter");
await expect(page.locator('#main, [role="main"]').first()).toBeFocused();
});
POUR 原則のリファレンス
| 原則 | 注目領域 | テスト例 |
|---|---|---|
| 知覚可能 | 代替テキスト、キャプション、コントラスト、構造 | 画像の代替テキスト、色のコントラスト比 |
| 操作可能 | キーボード、フォーカス、タイミング、ナビゲーション | Tab の順序、フォーカスの可視性、スキップリンク |
| 理解可能 | ラベル、説明、エラー、一貫性 | フォームのラベル、エラーメッセージ、予測可能な動作 |
| 堅牢 | 有効な HTML、ARIA、名前/ロール/値 | セマンティックな構造、アクセシブルな名前 |
Axe-Core タグ
デフォルト:wcag2a、wcag2aa、wcag21a、wcag21aa、wcag22a、wcag22aa(WCAG 2.2 AA)。追加チェックには best-practice を使用します。タグの全一覧は references/axe-tags-reference.md を参照してください。
例外処理
例外が避けられない場合:
- 対象を限定する - 特定のコンポーネント/ルートのみ
- 影響を記録する - 該当する WCAG の達成基準、ユーザーへの影響
- 期限を設定する - 担当者と修正日
- チケットを追跡する - 修正課題へのリンク
// [no] Avoid: Global rule disable
new AxeBuilder({ page }).disableRules(["color-contrast"]);
// [ok] Better: Scoped exclusion with documentation
new AxeBuilder({ page })
.exclude("#third-party-widget") // Known issue: JIRA-1234, fix by Q2
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22a", "wcag22aa"])
.analyze();
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| Axe で違反が 0 件と表示されるが、手動監査に不合格 | 自動化でカバーできるのは約 30~40% | 手動テストのチェックリストを追加する |
| 動的コンテンツで誤検知が発生する | コンテンツが完全に描画されていない | スキャン前に状態が安定するまで待つ |
| 色のコントラストが誤って不合格になる | 背景画像/グラデーション | 既知の誤検知には exclude を使用する |
| ロールで要素を見つけられない | セマンティック HTML が不足している | マークアップを修正する - これは実際のバグです |
| フォーカスが表示されない | :focus スタイルが不足している | 視認できるフォーカスインジケーター CSS を追加する |
| ダイアログでフォーカスがトラップされない | フォーカストラップのロジックが不足している | フォーカストラップを実装する(スニペットを参照) |
| スキップリンクが機能しない | 対象に tabindex="-1" がない | メインコンテンツに tabindex を追加する |
CLI クイックリファレンス
| コマンド | 説明 |
|---|---|
npx playwright test --grep "a11y" | アクセシビリティテストのみを実行する |
npx playwright test --headed | デバッグ用にブラウザーを表示して実行する |
npx playwright test --debug | Inspector を使ってステップ実行する |
PWDEBUG=1 npx playwright test | 一時停止を有効にしたデバッグモード |
注意すべき兆候
- axe スキャンで問題がないことを WCAG に完全準拠していると見なすこと — 自動化でカバーできるのは基準の約 30~40%のみです。
- ルールを全体的に無効化すること(例:
color-contrast)。チケットを記録したうえで、対象を限定した.exclude()を使用してください。 - ページの状態が安定する前にスキャンすること — 非同期コンテンツによって、誤って「違反 0 件」と表示されます。
- axe のテストに合格したからとキーボード/フォーカスのテストを省略すること — フォーカス順序とフォーカストラップには明示的なテストが必要です。
参考資料
| ドキュメント | 内容 |
|---|---|
| スニペット:セットアップとスキャン | axe-core のセットアップ、ヘルパー、スキャンのパターン |
| スニペット:キーボード、フォーカス、セマンティクス | キーボード操作、フォーカス管理、セマンティックな構造 |
| スニペット:視覚、名前、チェックリスト | 視覚的なアクセシビリティ、アクセシブルな名前、重要なページ |
| WCAG 2.2 AA チェックリスト | POUR の原則に基づく手動監査チェックリスト |
| ARIA パターン:ウィジェット 1 | 基礎、ダイアログ、タブ、メニューウィジェット |
| ARIA パターン:ウィジェット 2 | アコーディオン、コンボボックス、ライブリージョン、ツールチップ |
| ARIA パターン:誤りとリファレンス | よくある ARIA の誤りとロールのクイックリファレンス |
外部リソース
| リソース | URL |
|---|---|
| WCAG 2.2 仕様 | https://www.w3.org/TR/WCAG22/ |
| WCAG クイックリファレンス | https://www.w3.org/WAI/WCAG22/quickref/ |
| WAI-ARIA オーサリングプラクティス | https://www.w3.org/WAI/ARIA/apg/ |
| axe-core ルール | https://dequeuniversity.com/rules/axe/ |
検証
- axe-core の監査に合格 —
AxeBuilder.analyze()が重大な違反を 0 件返します - キーボード操作をテスト済み — すべてのインタラクティブ要素に Tab キーでアクセスでき、フォーカス順序が論理的です
- 十分な色のコントラスト — WCAG 2.2 AA の最小コントラスト比を満たしています(通常のテキストは 4.5:1、大きなテキストは 3:1)
- WCAG 2.2 AA に準拠 — スキャンに
wcag22a/wcag22aaタグが含まれています(フォーカスが隠れないこと、ドラッグ操作、ターゲットサイズの最小値)