Webアクセシビリティは「対応したほうがいい」から「対応しなければならない」へと変わりつつあります。2024年4月の障害者差別解消法改正で民間企業にも合理的配慮の提供が義務化され、EUではEuropean Accessibility Act(EAA)が2025年6月に施行されました。
Claude Codeを使うと、アクセシビリティ対応を開発ワークフローに組み込むことができます。axe-coreによる自動テスト、eslint-plugin-jsx-a11yによる静的解析、Pa11y CIによるCI/CD統合——これらをCLAUDE.mdに設定しておけば、Claude Codeが生成するコードが最初からWCAG 2.2 AA準拠になります。この記事では、その具体的な方法を解説します。
WCAG 2.2の概要 ── 何を満たせばよいのか
WCAG(Web Content Accessibility Guidelines)2.2は2023年10月にW3C勧告となった最新のアクセシビリティ基準です。実務ではAA準拠が目標になります。
WCAG 2.2で追加された主なAA基準は以下の4つです。
| 基準 | レベル | 内容 |
|---|---|---|
| 2.4.11 Focus Not Obscured | AA | キーボードフォーカスを受けた要素が完全に隠れてはいけない |
| 2.5.7 Dragging Movements | AA | ドラッグ操作にはシングルポインタの代替手段が必要 |
| 2.5.8 Target Size (Minimum) | AA | タッチターゲットは最小24×24 CSSピクセル |
| 3.3.8 Accessible Authentication | AA | 認証で認知機能テスト(CAPTCHA等)を要求しない |
CLAUDE.mdにアクセシビリティルールを記述する
Claude Codeにアクセシビリティ意識を「常に」持たせるには、CLAUDE.mdにルールを明記します。
## アクセシビリティ基準(WCAG 2.2 AA準拠) ### 必須ルール - すべての画像にalt属性を付与(装飾画像はalt="") - インタラクティブ要素にはaria-labelまたはvisible labelを必須 - 色コントラスト比: 通常テキスト4.5:1以上、大テキスト(18px+ bold/24px+)3:1以上 - キーボード操作: すべての機能がキーボードのみで操作可能 - フォーカスインジケータ: カスタムスタイル時も視認可能に(outline: none禁止) - タッチターゲット: 最小24x24 CSSピクセル(WCAG 2.5.8) - フォームエラー: aria-describedbyでエラーメッセージをプログラム的に関連付け - 見出し階層: h1→h2→h3の順で飛ばさない - ランドマーク: main, nav, header, footer を適切に使用 ### 自動チェックツール - ESLint: eslint-plugin-jsx-a11y(strictモード) - テスト: @axe-core/playwright でE2Eテスト時にa11yチェック - Storybook: addon-a11yでコンポーネント単位チェック - CI: Pa11y CIでPR時に自動チェック ### 禁止パターン - div/spanにonClick付与(button要素を使用すること) - autofocusの無秩序な使用 - aria-hiddenをフォーカス可能な要素に付与 - tabindex > 0 の使用 - CSSのoutline: noneでフォーカスインジケータ削除
eslint-plugin-jsx-a11yで静的解析する
コーディング時点でアクセシビリティ違反を検出する最も効率的な方法は、ESLintの静的解析です。
eslint-plugin-jsx-a11y をstrictモードで導入してください。
要件:
- Flat Config(eslint.config.js)形式
- strictプリセット(recommendedより厳格)
- 以下を error に設定:
- alt-text, aria-props, aria-role, click-events-have-key-events,
heading-has-content, label-has-associated-control
- 設定後に全ファイルでlintを実行してエラー一覧を出してください
import jsxA11y from "eslint-plugin-jsx-a11y";
export default [
// ... other config
{
plugins: { "jsx-a11y": jsxA11y },
rules: {
...jsxA11y.flatConfigs.strict.rules,
// 追加で厳格にする項目
"jsx-a11y/alt-text": "error",
"jsx-a11y/click-events-have-key-events": "error",
"jsx-a11y/no-noninteractive-element-interactions": "error",
"jsx-a11y/label-has-associated-control": ["error", {
controlComponents: ["Input", "Select", "Textarea"],
}],
},
},
];
axe-core + Playwrightでアクセシビリティを自動テストする
@axe-core/playwrightを使うと、E2EテストにWCAGチェックを組み込めます。ページ遷移のたびにアクセシビリティ違反を自動検出します。
npm install --save-dev @axe-core/playwright
import { test, expect } from "@playwright/test";
import AxeBuilder from "@axe-core/playwright";
test.describe("アクセシビリティ", () => {
test("トップページがWCAG 2.2 AA準拠", async ({ page }) => {
await page.goto("/");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
.analyze();
expect(results.violations).toEqual([]);
});
test("フォームページがWCAG 2.2 AA準拠", async ({ page }) => {
await page.goto("/contact");
const results = await new AxeBuilder({ page })
.withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"])
.exclude("#third-party-widget") // サードパーティ部分は除外
.analyze();
expect(results.violations).toEqual([]);
});
});
Pa11y CIでCI/CDにアクセシビリティゲートを設置する
{
"defaults": {
"standard": "WCAG2AA",
"runners": ["axe"],
"timeout": 10000,
"viewport": { "width": 1280, "height": 1024 }
},
"urls": [
"http://localhost:3000/",
"http://localhost:3000/about",
"http://localhost:3000/contact",
"http://localhost:3000/products"
],
"concurrency": 4
}
name: Accessibility Check
on: pull_request
jobs:
a11y:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build
- name: Start preview server
run: npm run preview &
- name: Wait for server
run: npx wait-on http://localhost:3000
- name: Run Pa11y CI
run: npx pa11y-ci
GitHub Actionsの詳しい設定はClaude Code GitHub Actions完全ガイドをご覧ください。
Storybook addon-a11yでコンポーネント単位のチェック
npx storybook add @storybook/addon-a11y
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
a11y: {
options: {
runOnly: {
type: "tag",
values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"],
},
},
},
},
};
export default preview;
const meta = {
component: Button,
parameters: {
a11y: {
test: "error", // "error" = テスト失敗 / "todo" = 警告のみ / "off" = スキップ
},
},
} satisfies Meta<typeof Button>;
Storybook MCPとの連携はClaude Code × Storybook完全ガイドで解説しています。
Claude Codeにアクセシビリティ違反を自動修正させる
axe-coreやPa11yで検出された違反をClaude Codeに渡すと、具体的な修正コードを生成できます。
Pa11y CIの結果、以下のWCAG違反が検出されました。 すべて修正してください。 1. /about: img要素にalt属性がない(WCAG 1.1.1) 2. /contact: フォームのinputにlabelが関連付けされていない(WCAG 1.3.1) 3. /products: テキストの色コントラスト比が3.2:1(AA基準4.5:1未満)(WCAG 1.4.3) 4. /: ボタンがdiv+onClickで実装されている(WCAG 2.1.1) 5. /: フォーカスインジケータがCSSでoutline:noneされている(WCAG 2.4.7) 各違反について修正コードを生成してください。
よくある違反パターンと修正例
// NG: divにonClick(キーボード操作不可)
<div onClick={handleAction} className="btn">
送信
</div>
// OK: button要素(キーボード操作・フォーカス・スクリーンリーダー自動対応)
<button onClick={handleAction} className="btn">
送信
</button>
// NG: スクリーンリーダーが「ボタン」としか読み上げない
<button onClick={onClose}><XIcon /></button>
// OK: ボタンの目的を伝える
<button onClick={onClose} aria-label="閉じる"><XIcon /></button>
// NG: labelとinputが関連付けされていない <label>メールアドレス</label> <input type="email" name="email" /> // OK: htmlForとidで関連付け <label htmlFor="email">メールアドレス</label> <input type="email" id="email" name="email" /> // OK: labelでinputをラップ <label> メールアドレス <input type="email" name="email" /> </label>
import { useRef, useEffect } from "react";
interface DialogProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
export function Dialog({ isOpen, onClose, title, children }: DialogProps) {
const dialogRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) dialogRef.current?.focus();
}, [isOpen]);
if (!isOpen) return null;
return (
<div
ref={dialogRef}
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
tabIndex={-1}
onKeyDown={(e) => e.key === "Escape" && onClose()}
>
<h2 id="dialog-title">{title}</h2>
{children}
<button onClick={onClose}>閉じる</button>
</div>
);
}
よくある質問
["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"]を指定します。これでWCAG 2.0/2.1/2.2のA・AA基準がすべてチェックされます。ベストプラクティスも含めたい場合は"best-practice"タグを追加してください。まとめ
- CLAUDE.mdにルール明記: アクセシビリティ基準を書いておくと、Claude Codeの生成コードが最初からWCAG準拠になる
- eslint-plugin-jsx-a11y: コーディング時点でalt漏れ・label未関連付け・div+onClick等を静的検出
- axe-core + Playwright: E2EテストにWCAGチェックを統合。ページ遷移ごとに自動検出
- Pa11y CI + GitHub Actions: PRごとにアクセシビリティゲートを設置。違反を含むPRをブロック
- Storybook addon-a11y: コンポーネント単位でWCAGチェック
- 自動テストの限界: 検出率は約57%。手動テスト(スクリーンリーダー+キーボード操作)との併用が必須
テスト戦略全般はClaude Codeテスト完全ガイド、フロントエンド開発はClaude Codeフロントエンド開発ガイド、Storybook連携はClaude Code × Storybook完全ガイドもあわせてご覧ください。
