mcpField
W
web-accessibility-web-accessibility

web-accessibility-web-accessibility

github.com/agents-incデザイン・UI26103 回インストール2 日前に更新
リポジトリ

web-accessibility-web-accessibility について

デザイナーやフロントエンド開発者向けの、Webアクセシビリティ実践ガイドです。WCAG 2.2 AAを軸に、セマンティックHTML、キーボード操作、フォーカス、コントラスト、支援技術でのテストを扱います。

主な機能

  1. 01セマンティックHTMLを優先
  2. 02キーボード操作とフォーカスの確認
  3. 03コントロールにアクセシブルな名前を付与
  4. 04テキストとUIのコントラスト要件
  5. 05動的通知と色に依存しない状態表示

活用シーン

  1. 01キーボード操作可能なページの設計
  2. 02フォーム項目とエラーのアクセシビリティ対応
  3. 03ダイアログのフォーカス管理
  4. 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.yaml
reference.md
examples/
color.md
core.md
focus.md
forms.md
screen-reader.md
tables.md
testing.md
touch-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>

コメント