accessibility について
WCAG 2.2 と Lighthouse に基づく、開発者や監査担当者向けのアクセシビリティスキルです。ページの実測結果にキーボード操作と手動確認を組み合わせ、自動スコアだけで適合性を判断しません。
主な機能
- 01WCAG 2.2 のガイド
- 02Lighthouse と axe の監査手順
- 03アクセシビリティツリーとキーボードの確認
- 04実装パターン
- 05手動テストのチェックリスト
活用シーン
- 01レンダリング済みページの問題監査
- 02監査結果をコンポーネントやテンプレートに特定
- 03キーボード操作とフォーカス挙動の確認
MCP 対応
Chrome DevTools MCP が使える場合、`lighthouse_audit` と `take_snapshot` でレンダリング後のアクセシビリティ上の問題を調べます。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ addyosmani/ web-quality-skills --skill accessibilityインストール後、Claude Code で/accessibilityを入力して読み込まれたか確認します。
同梱ファイルreferences/A11Y-PATTERNS.mdreferences/WCAG.md
詳細
アクセシビリティ(a11y)
WCAG 2.2 および Lighthouse のアクセシビリティ監査に基づく包括的なアクセシビリティガイドラインです。障害のある人を含むすべての人がコンテンツを利用できるようにすることを目指します。
エビデンスに基づく監査ワークフロー
レンダリングされたページを利用できる場合:
- 利用可能であれば、Lighthouse のアクセシビリティ監査を実際のページで実行します。Chrome DevTools MCP では、
lighthouse_auditを使用します。一般公開ページではモバイルのナビゲーションモードを、再読み込みによって認証済みの状態やユーザーが作成した状態が失われる場合はスナップショットモードを使用します。 - 監査で失敗したノードを使って関連するコンポーネントやテンプレートを特定し、リポジトリ全体から一般的なパターンを探すのは避けます。
- レンダリングされたアクセシビリティツリーのスナップショットを調べ、名前、ロール、状態、ランドマーク、見出し構造を確認します。Chrome DevTools MCP では、
take_snapshotを使用します。キーボードで対象のフローを操作します。 - ソースを修正したら、同じ監査と手動での操作確認を再度実行します。
実際のページで使用できるツールがない場合は、Lighthouse CLI または axe を使って自動テストを行い、同じ手動確認を完了します。自動ツールで検出できるアクセシビリティ上の障壁は一部に限られます。スコアが 100 でも WCAG 準拠を意味するわけではなく、スコアが低くても個々の問題に関するエビデンスの代わりにはなりません。
WCAG の原則:POUR
| 原則 | 説明 |
|---|---|
| Perceivable(知覚可能) | さまざまな感覚を通じてコンテンツを知覚できます |
| Operable(操作可能) | すべてのユーザーがインターフェースを操作できます |
| Understandable(理解可能) | コンテンツとインターフェースを理解できます |
| Robust(堅牢) | コンテンツは支援技術で機能します |
適合レベル
| レベル | 要件 | 目標 |
|---|---|---|
| A | 最低限のアクセシビリティ | 適合必須 |
| AA | 標準的な適合性 | 適合が望ましい(多くの法域で法的要件) |
| AAA | 高度なアクセシビリティ | できれば対応 |
知覚可能
テキストによる代替(1.1)
画像には alt テキストが必要です:
<img src="chart.png">
<img src="chart.png" alt="Bar chart showing 40% increase in Q3 sales">
<img src="decorative-border.png" alt="" role="presentation">
<figure>
<img src="infographic.png" alt="2024 market trends infographic"
aria-describedby="infographic-desc">
<figcaption id="infographic-desc">
</figcaption>
</figure>
アイコンボタンにはアクセシブルな名前が必要です:
<button><svg></svg></button>
<button aria-label="Open menu">
<svg aria-hidden="true"></svg>
</button>
<button>
<svg aria-hidden="true"></svg>
<span class="visually-hidden">Open menu</span>
</button>
視覚的に隠すクラス:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
色のコントラスト(1.4.3、1.4.6)
| テキストサイズ | AA の最低基準 | AAA の強化基準 |
|---|---|---|
| 通常のテキスト(< 18px / < 14px 太字) | 4.5:1 | 7:1 |
| 大きなテキスト(≥ 18px / ≥ 14px 太字) | 3:1 | 4.5:1 |
| UI コンポーネントとグラフィック | 3:1 | 3:1 |
/* ❌ Low contrast (2.5:1) */
.low-contrast {
color: #999;
background: #fff;
}
/* ✅ Sufficient contrast (7:1) */
.high-contrast {
color: #333;
background: #fff;
}
/* ✅ Focus states need contrast too (3:1 against background, WCAG 1.4.11) */
:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
色だけに頼らないでください:
<input class="error-border">
<style>.error-border { border-color: red; }</style>
<div class="field-error">
<input aria-invalid="true" aria-describedby="email-error">
<span id="email-error" class="error-message">
<svg aria-hidden="true"></svg>
Please enter a valid email address
</span>
</div>
メディアの代替(1.2)
<video controls>
<source src="video.mp4" type="video/mp4">
<track kind="captions" src="captions.vtt" srclang="en" label="English" default>
<track kind="descriptions" src="descriptions.vtt" srclang="en" label="Descriptions">
</video>
<audio controls>
<source src="podcast.mp3" type="audio/mp3">
</audio>
<details>
<summary>Transcript</summary>
<p>Full transcript text...</p>
</details>
操作可能
キーボードでの操作(2.1)
すべての機能をキーボードで操作できる必要があります。 ネイティブのインタラクティブ要素(<button>、<a href>、フォームコントロール)を優先してください。これらは Enter/Space による操作、フォーカス、支援技術向けのセマンティクスに標準で対応しています。ネイティブ要素を使用できない場合に限り、手動でのキーボード操作を追加してください。
<div class="card" onclick="handleAction()">Open</div>
<button type="button" onclick="handleAction()">Open</button>
// ✅ When you MUST use a non-interactive element (e.g. div with role="button"),
// make it focusable AND handle keyboard activation. Do NOT add this to a native
// <button> — Enter/Space already fire click, so you'd double-trigger.
element.setAttribute('role', 'button');
element.setAttribute('tabindex', '0');
element.addEventListener('click', handleAction);
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleAction();
}
});
キーボードトラップを作らないでください。 ユーザーはすべてのコンポーネントに Tab キーで移動して入ったり出たりできる必要があります。ダイアログにはモーダルのフォーカストラップパターンを使用してください。ネイティブの <dialog> 要素はこれを自動的に処理します。
フォーカスの可視化(2.4.7)
/* ❌ Never remove focus outlines */
*:focus { outline: none; }
/* ✅ Use :focus-visible for keyboard-only focus */
:focus {
outline: none;
}
:focus-visible {
outline: 2px solid currentColor; /* inherits text color → already contrast-checked */
outline-offset: 2px;
}
/* ✅ Or pick a brand color and verify ≥3:1 contrast against every background it lands on */
button:focus-visible {
box-shadow: 0 0 0 3px rgba(0, 95, 204, 0.5);
}
フォーカスが隠れないこと(2.4.11)— 2.2 で新設
要素がキーボードフォーカスを受けたとき、固定ヘッダー、フッター、重なり合うパネルなど、作成者が配置した他のコンテンツによって完全に隠れてはなりません。レベル AAA(2.4.12)では、フォーカスされた要素の一部も隠れてはなりません。
/* ✅ Account for sticky headers when scrolling to focused elements */
:target {
scroll-margin-top: 80px;
}
/* ✅ Ensure focused items clear fixed/sticky bars */
:focus {
scroll-margin-top: 80px;
scroll-margin-bottom: 60px;
}
スキップリンク(2.4.1)
キーボードを使うユーザーが繰り返し表示されるナビゲーションをスキップできるよう、スキップリンクを用意します。マークアップとスタイルの詳細については、スキップリンクパターンを参照してください。
ターゲットサイズ(2.5.8)— 2.2 で新設
インタラクティブなターゲットは、24 × 24 CSS ピクセル以上である必要があります(AA)。例外:インラインのテキストリンク、ブラウザーがサイズを制御する要素、および境界ボックスの中心に置いた直径 24px の円が他のターゲットと重ならないターゲット。
/* ✅ Minimum target size */
button,
[role="button"],
input[type="checkbox"] + label,
input[type="radio"] + label {
min-width: 24px;
min-height: 24px;
}
/* ✅ Comfortable target size (recommended 44×44) */
.touch-target {
min-width: 44px;
min-height: 44px;
display: inline-flex;
align-items: center;
justify-content: center;
}
ドラッグ操作(2.5.7)— 2.2 で新規追加
ドラッグが必要な操作には、必ずシングルポインターで操作できる代替手段(ボタンや入力欄など)を用意してください。並べ替え可能なリストの例については、ドラッグ操作のパターンを参照してください。
タイミング(2.2)
// Allow users to extend time limits
function showSessionWarning() {
const modal = createModal({
title: 'Session Expiring',
content: 'Your session will expire in 2 minutes.',
actions: [
{ label: 'Extend session', action: extendSession },
{ label: 'Log out', action: logout }
],
timeout: 120000
});
}
モーション(2.3)
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
理解可能
ページの言語(3.1.1)
<html>
<html lang="en">
<p>The French word for hello is <span lang="fr">bonjour</span>.</p>
一貫したナビゲーション(3.2.3)
<nav aria-label="Main">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
一貫したヘルプ(3.2.6)— 2.2 で新規追加
ヘルプ機能(連絡先情報、チャットウィジェット、FAQリンク、セルフヘルプの選択肢)が複数のページに繰り返し表示される場合は、毎回同じ相対的な順序で表示する必要があります。配置の一貫性を頼りにするユーザーが、ページを開くたびにヘルプを探し回る必要がないようにしてください。
フォームのラベル(3.3.2)
すべての入力欄には、プログラムで関連付けられたラベルが必要です。明示的、暗黙的、および説明付きの例については、フォームのラベルのパターンを参照してください。
エラー処理(3.3.1、3.3.3)
role="alert" または aria-live を使用してエラーをスクリーンリーダーに通知し、無効なフィールドには aria-invalid="true" を設定し、送信時には最初のエラーにフォーカスを移動してください。マークアップと JS の全例については、エラー処理のパターンを参照してください。
再入力の防止(3.3.7)— 2.2 で新規追加
同じセッションですでに入力した情報を、ユーザーに再入力させないでください。前のステップの情報を自動入力するか、以前に入力した値から選択できるようにしてください。例外:セキュリティ上の再確認、および有効期限が切れたコンテンツ。
<fieldset>
<legend>Shipping address</legend>
<label>
<input type="checkbox" id="same-as-billing" checked>
Same as billing address
</label>
</fieldset>
アクセシブルな認証(3.3.8)— 2.2 で新規追加
ログインフローでは、次のいずれかを満たさない限り、認知機能テスト(パスワードの記憶やパズルの解答など)に依存してはなりません。
- コピー&ペーストまたは自動入力の仕組みが利用できる
- 代替方法がある(パスキー、SSO、メールリンクなど)
- テストでオブジェクト認識または個人的なコンテンツを使用する(AA のみ。AAA ではこの例外は認められません)
<input type="password" id="password" autocomplete="current-password">
<button type="button">Sign in with passkey</button>
<button type="button">Email me a login link</button>
堅牢
ARIA の使用(4.1.2)
ネイティブ要素を優先してください:
<div role="button" tabindex="0">Click me</div>
<button>Click me</button>
<div role="checkbox" aria-checked="false">Option</div>
<label><input type="checkbox"> Option</label>
**ARIA が必要な場合は、**適切なロールと状態を使用してください。タブリストの完全な例については、ARIA タブのパターンを参照してください。
ライブリージョン(4.1.3)
フォーカスを移動せずに動的なコンテンツの変更を通知するには、aria-live リージョンを使用してください。マークアップと showNotification() ヘルパーについては、ライブリージョンのパターンを参照してください。
テストチェックリスト
自動テスト
失敗したレンダリング済みノードをエージェントに直接返す、実行中の Lighthouse 監査を優先してください。Chrome DevTools MCP を使用する場合は lighthouse_audit です。それ以外の場合は、次のとおりです。
# Lighthouse accessibility audit
npx lighthouse https://example.com --only-categories=accessibility
# axe-core
npm install @axe-core/cli -g
axe https://example.com
手動テスト
- キーボードナビゲーション: ページ全体を Tab キーで移動し、Enter/Space キーで操作を実行する
- スクリーンリーダー: VoiceOver(Mac)、NVDA(Windows)、または TalkBack(Android)でテストする
- ズーム: 200% のズームでもコンテンツを利用できる
- ハイコントラスト: Windows のハイコントラストモードでテストする
- モーションの軽減:
prefers-reduced-motion: reduceでテストする - フォーカス順序: 論理的で、視覚的な順序に沿っている
- ターゲットサイズ: インタラクティブ要素が最小サイズ 24×24px を満たしている
VoiceOver と NVDA のショートカットについては、スクリーンリーダーコマンドのリファレンスを参照してください。
影響度別の一般的な問題
重大(直ちに修正)
- フォームのラベルがない
- 画像の代替テキストがない
- 色のコントラストが不十分
- キーボードトラップがある
- フォーカスインジケーターがない
深刻(公開前に修正)
- ページの言語が指定されていない
- 見出し構造がない
- リンクテキストが説明的でない
- メディアが自動再生される
- スキップリンクがない
中程度(早めに修正)
- アイコンに ARIA ラベルがない
- ナビゲーションが一貫していない
- エラーが特定されていない
- タイミングを制御できない
- ランドマークリージョンがない