web-accessibility-web-accessibility について
デザイナーやフロントエンド開発者向けの、Webアクセシビリティ実践ガイドです。WCAG 2.2 AAを軸に、セマンティックHTML、キーボード操作、フォーカス、コントラスト、支援技術でのテストを扱います。
主な機能
- 01セマンティックHTMLを優先
- 02キーボード操作とフォーカスの確認
- 03コントロールにアクセシブルな名前を付与
- 04テキストとUIのコントラスト要件
- 05動的通知と色に依存しない状態表示
活用シーン
- 01キーボード操作可能なページの設計
- 02フォーム項目とエラーのアクセシビリティ対応
- 03ダイアログのフォーカス管理
- 04WCAG 2.2 AAに沿った画面レビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agents-inc/ skills --skill web-accessibility-web-accessibilityインストール後、Claude Code で/web-accessibility-web-accessibilityを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
metadata.yamlreference.mdexamples/color.mdcore.mdfocus.mdforms.mdscreen-reader.mdtables.mdtesting.mdtouch-targets.md詳細
アクセシビリティ
クイックガイド: アクセシビリティの大部分は、適切な要素を選ぶことで解決できます。
<button>にはフォーカス、キーボード操作、ロールが備わっていますが、<div>でそれらを再現するには何十行ものコードが必要です。一方、要素だけでは補えないこともあります。すべてのコントロールに名前を付けること、視認できるフォーカスインジケーター、テキストのコントラスト比 4.5:1、コンテンツの変更時の通知、そして色だけに情報を担わせないことです。WCAG 2.2 レベル AA を目指してください。自動チェックで検出できる問題はおよそ半数なので、キーボードでの確認は必須です。
詳細なリソース:
- examples/core.md — スキップリンク、ランドマーク、セマンティックな構造、ボタンとリンクの使い分け
- examples/forms.md — ラベル、エラー処理、必須項目、リストボックスの仕様
- examples/focus.md — ダイアログ、フォーカスのトラップと復元、フォーカスインジケーター
- examples/color.md — コントラスト、色に依存しないステータス表示、リンクのスタイル
- examples/tables.md — 見出し、キャプション、並べ替え可能な列
- examples/touch-targets.md — ターゲットのサイズと間隔
- examples/screen-reader.md — 視覚的に隠されたテキスト、装飾的なコンテンツ
- examples/testing.md — 自動監査、ロールベースのクエリ、手動確認
- reference.md — WCAG 2.2 の達成基準、アクセシブルな名前のルール、監査ルール ID、スクリーンリーダー
<critical_requirements>
UI コードを書く前に
ARIA 属性より先に、意味のある要素を使います。 <button>、<a href>、<nav>、<table> には、適切なロール、キーボード操作、フォーカスがすでに備わっています。これらはどれも <div> では手作業で再構築する必要があり、その再構築こそが失敗の原因になります。
すべてのインタラクティブ要素にキーボードでアクセスできるようにし、フォーカスを視覚的に示します。 キーボードを使うユーザーはフォーカスリングを手がかりに移動するため、代替表示なしに outline: none を指定すると、ページ内を手探りで進むことになります。
すべてのコントロールに、スクリーンリーダーで読み上げられる名前を付けます。 名前のないアイコンのみのボタンは「ボタン」としか読み上げられません。そこに何かがあることは伝わっても、何をするものなのかは伝わりません。
テキストと背景のコントラスト比は 4.5:1 以上にし、UI の境界線やフォーカスインジケーターは 3:1 以上にします。 これを下回ると、明るい場所や安価なディスプレイでは文字が読めず、コントラスト感度が低下している人にも読めません。いずれ多くの人がそうなります。
色による情報には、必ず形、アイコン、または言葉を組み合わせます。 エラーを赤色だけで示しても、赤と緑を見分けにくい人には何も伝わらず、スクリーンリーダーでもまったく伝わりません。
ページ遷移なしに表示されるコンテンツは、読み上げられるようにします。 検証エラー、保存完了の確認、読み込み結果は、ライブリージョンで通知しない限り、どれも読み上げられません。
</critical_requirements>
自動検出: accessibility、a11y、WCAG、ARIA、aria-label、aria-labelledby、aria-describedby、aria-live、aria-expanded、aria-invalid、role 属性、キーボードナビゲーション、フォーカス管理、フォーカストラップ、focus-visible、スキップリンク、ランドマーク、スクリーンリーダー、sr-only、色のコントラスト、prefers-reduced-motion、tabindex、axe
対象範囲:
- キーボードでのアクセス、タブ順序、フォーカスの移動、フォーカスインジケーター
- ARIA のロール、状態、プロパティ、および要素によってそれらが不要になる場合
- コントラスト比と色に依存しない情報提示
- アクセシブルな名前、ライブリージョン、読み上げのタイミング
- ターゲットサイズ、動きの設定、およびそれらを追加した WCAG 2.2 の達成基準
別の場所で扱う内容:
- 複雑なウィジェットのコンポーネント内部 — テスト済みのプリミティブライブラリが以下の ARIA コントラクトを実装します。このスキルではコントラクトを示し、使用するライブラリを確認できるようにします。
- ビジュアルデザイントークン — カラーパレットは別の場所で設計され、このスキルでは満たすべき比率を示します。
- テストランナーの仕組み — 監査の実行はテストツールの役割であり、ここではアサーションの内容を示します。
- コンテンツの作成 — 読解レベル、平易な表現、キャプションの文章は、マークアップではなくライティング上の判断事項です。
哲学
アクセシブルなインターフェースは、別のバリエーションではありません。意味をすでに備えた要素で構築し、ブラウザーが推測できない部分を明示した、同じインターフェースです。
この考え方に立つと、作業の大半は削ぎ落とすことになります。セマンティック HTML がアクセシビリティの基盤です。本来の要素を置き換える <div> は、いずれも ARIA 属性、キーイベントハンドラー、フォーカス管理で返済する負債となり、しかも十分に返済できないことがあります。以下の属性は、意味を担う要素が存在しない場合のためのものです。その意味を担えるマークアップの上に重ねるものではありません。
まずキーボードで使えるようにします。ポインターでできることはすべてキーボードでもできなければなりません。また、マウスなしで使えるデザインは、マウスを使っても問題なく使えます。
<decision_framework>
ケースで必要な仕組み
What is being built?
├─ Something that acts when activated → <button>
│ └─ Icon only → add an accessible name
├─ Something that navigates → <a href>
│ └─ Current destination → aria-current="page"
├─ A structural region → <nav>, <main>, <header>, <aside>, <footer>
│ └─ More than one of a kind → label each with aria-label
├─ A complex widget — dialog, listbox, tabs, combobox, tree
│ └─ Take a tested primitive. The ARIA contract is large,
│ and a partial implementation reads as broken rather than absent.
└─ Content that appears without navigation
├─ An error or something urgent → role="alert"
└─ Progress or confirmation → role="status"
ARIAを使うかどうか: ARIAの第一のルールは、ARIAを使わないことです。role="button" を <div> に付けると、スクリーンリーダーには正しい名称が伝わりますが、キーボードには何もできません。そのため、イベントハンドラー、tabindex、フォーカス時のスタイルは、いずれも別途用意する必要があります。要素で表現できるものがない場合にARIAを使いましょう。たとえば、ライブリージョン、開閉要素の aria-expanded、列見出しの並び替え方向などです。
AAかAAAか: 適合目標はAAであり、規制でも通常参照されるのはAAです。AAAの基準には、コントラスト比7:1や44×44のターゲットサイズなど、実装コストが低いものも多く、デザインで可能な範囲で取り入れる価値があります。ただし、製品全体をAAAに準拠させる必要はありません。
</decision_framework>
基本パターン
パターン1:スキップリンク
ページ上で最初にフォーカス可能な要素です。フォーカスされるまで非表示にし、各ページで繰り返されるナビゲーションを飛び越えます。
<a href="#main-content" className="skip-link">Skip to main content</a>
…
<main id="main-content" tabIndex={-1}>{children}</main>
ターゲットに tabIndex={-1} を設定すると、フォーカスが実際にそこへ移動します。これがないと、フラグメントによってビューポートだけが移動し、フォーカスはドキュメントの先頭に残ります。
完全なコード:examples/core.md
パターン2:ランドマーク
ランドマーク要素を使うと、スクリーンリーダーのユーザーは直線的に読み進めるのではなく、領域間を移動できます。
<header>…</header>
<nav aria-label="Main">…</nav>
<main>…</main>
<aside aria-label="Related">…</aside>
<footer>…</footer>
ページごとに <main> は1つにします。同じ種類のランドマークが2つある場合、それぞれを区別するラベルが必要です。ラベルのない <nav> が2つあると、同じ名前で読み上げられます。
完全なコード:examples/core.md
パターン3:ボタンとリンクの使い分け
操作を実行するものはボタン、移動するものはリンクです。この違いによって、キーボードでの動作、読み上げられる内容、ブラウザー本来の機能が使えるかどうかが決まります。
<button onClick={handleSubmit}>Submit form</button>
<a href="/dashboard">Go to dashboard</a>
遷移する <button> では、「新しいタブで開く」操作、ステータスバーのURL表示、マウスの中ボタンによるクリックが使えなくなります。<div onClick> では、操作全体が使えなくなります。
完全なコード:examples/core.md
パターン4:アクセシブルな名前
すべてのコントロールには名前が必要です。アイコンのみのコントロールでは、名前が抜け落ちやすくなります。
<button aria-label="Delete item">
<TrashIcon aria-hidden="true" />
</button>
<button>
<TrashIcon aria-hidden="true" />
<span className="sr-only">Delete item</span>
</button>
どちらも「項目を削除、ボタン」と読み上げられます。2つ目は翻訳ツールで処理でき、DOMのテキスト検索でも見つかるため、どちらかが重要な場合は2つ目を優先してください。
解決順序はreference.mdを、完全なコードはexamples/screen-reader.mdを参照してください。
パターン5:フォームフィールドとエラー
入力欄に関連付けられたラベル、入力欄に設定する無効状態、入力欄から確認できるエラーメッセージが必要です。
<label htmlFor="email">Email</label>
<input
id="email"
type="email"
aria-required="true"
aria-invalid={Boolean(error)}
aria-describedby={error ? "email-error" : undefined}
/>
{error && <span id="email-error" role="alert">{error}</span>}
プレースホルダーはラベルではありません。入力を始めると消え、多くのデザインでコントラストも不足します。
完全なコード:examples/forms.md
パターン6:フォーカスインジケーター
:focus-visible を使うと、キーボード操作時にはフォーカスリングを表示し、マウスクリック時には表示しません。これにより、そもそもフォーカススタイルを削除する理由がなくなります。
.button:focus-visible {
outline: 2px solid var(--color-focus);
outline-offset: 2px;
}
太さは少なくとも2px、背後にあるものとのコントラスト比は少なくとも3:1にします。
完全なコード:examples/focus.md
パターン7:ダイアログとフォーカスの移動
モーダルを実装する担当者が誰であっても、満たすべき要件は同じです。開いたときにフォーカスが移動し、開いている間は外へ出ず、Escapeキーで閉じ、開いた要素にフォーカスが戻る必要があります。
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">{title}</h2>
…
</div>
最も見落とされやすいのは、トリガーにフォーカスを戻すことです。これがないと、キーボードユーザーは自分がどこにいたのかわからないまま、ドキュメントの先頭に戻されてしまいます。
完全なコード:examples/focus.md
パターン8:ライブリージョン
ページ遷移なしに表示されたコンテンツは、ライブリージョンが通知しない限り、読み上げられません。
<div role="status">{savedMessage}</div>
<div role="alert">{errorMessage}</div>
role="status" は一時停止を待って通知し、role="alert" は割り込んで通知します。メッセージが表示される前に、ライブリージョンがDOMに存在している必要があります。ライブリージョンとコンテンツを同時に挿入すると、何も読み上げられないことがよくあります。
パターン9:色だけに依存しないステータス表示
色は情報を補助するものであり、情報そのものにしてはいけません。
<span className={statusClass}>
<span aria-hidden="true">{status === "error" ? "×" : "✓"}</span>
<span>{status === "error" ? "Failed" : "Complete"}</span>
</span>
視覚的にはアイコンが情報を伝え、スクリーンリーダーにはテキストが伝えます。色をなくしても、どちらも機能します。
完全なコード:examples/color.md
### パターン 10:動きに関する設定動きを取り入れるのはオプトイン方式にし、動きを減らしたいと希望する読者には、デフォルトでその設定が適用されるようにします。
@media (prefers-reduced-motion: no-preference) {
.card {
animation: slide-in 300ms ease-out;
}
}
reduce は動きをなくすのではなく、最小限にするという意味です。スライドの代わりにフェードインを使えば、フィードバックを保ちながら、前庭系への刺激を抑えられます。
<red_flags>
注意すべき点
実行時に問題が発生します:
- 代替スタイルのない
outline: none— キーボードユーザーは現在の位置を完全に見失います。リングを削除するのではなく、:focus-visibleに限定してください。 onClickを持つ<div>または<span>— フォーカスできず、Enter キーや Space キーでも起動せず、何もないものとして読み上げられます。代わりに<button>を使ってください。<div>にrole="button"を指定する — 名前だけが付与され、動作は何も追加されません。そのため、Tab、Enter、Space キーへの対応は依然として必要です。- フォーカス可能な要素に
aria-hidden="true"を指定する — スクリーンリーダーが存在しないと言い張る要素に、フォーカスが移動してしまいます。 - 0 より大きい値の
tabindex— 要素がドキュメント全体の順序より前に置かれます。この値が1つでもあると、ページ全体の順序が乱れます。 - 関連付けられたラベルのない入力欄 — 種類だけが読み上げられるため、フォームは「テキスト編集、テキスト編集、テキスト編集」と読み上げられます。
予期しない動作:
- 装飾画像には
alt=""が正しい値です。alt自体を省略すると、スクリーンリーダーがファイル名を読み上げます。 - ライブリージョンをメッセージと同時に DOM に追加すると、通常は何も読み上げられません。先にコンテナーをレンダリングしてください。
- ライブリージョンで読み上げられるのは、変更された部分だけです。そのため、「全10件中3件の結果」がその場で更新されると、文脈なしで「5」と読み上げられることがあります。
aria-atomic="true"を指定すると、リージョン全体が再度読み上げられます。 role="alert"は読み上げ中の内容に割り込むため、キー入力ごとの検証メッセージに使うと実用的ではありません。- プレースホルダーはラベルではなく、読者が最も必要とするタイミングで消えてしまいます。
- 無効化されたボタンはフォーカスの対象外となるため、フォームが有効になるまで無効化される送信ボタンでは、その理由を確認する手段がありません。
- 入力欄を囲む
<label>も、for/htmlForによる関連付けも機能します。ただし、1つの入力欄に2つのラベルを付けると正しく動作せず、読み上げられるのは1つだけです。 prefers-reduced-motion: reduceは動きをなくすのではなく、減らすよう求めます。重要なフィードバックまで取り除くのはやりすぎです。- 自動監査で検出できる WCAG の違反は、およそ半分です。順序や文言、そして読み上げが適切だったかどうかは、人が確認する必要があります。
</red_flags>