mcpField
A
accessibility

accessibility

github.com/jezwebデザイン・UI1.1k505 回インストール3 週間前に更新
リポジトリ

accessibility について

accessibility は、WCAG 2.1 AA を軸にした、ウェブデザイナーと開発者向けのフレームワーク非依存スキルです。セマンティック HTML、キーボード操作、コントラスト、フォーム、テストの指針を扱います。

主な機能

  1. 01セマンティック HTML と ARIA の指針
  2. 02キーボード操作とフォーカス管理
  3. 03WCAG AA の色コントラスト要件
  4. 04アクセシブルなフォームとエラーメッセージ
  5. 05キーボード・スクリーンリーダー・自動テスト

活用シーン

  1. 01ウェブサイトの WCAG 2.1 AA 適合確認
  2. 02ダイアログ、タブ、フォームのアクセシビリティ改善
  3. 03キーボードやスクリーンリーダー利用者向けの操作調整
  4. 04自動ツールによるアクセシビリティ問題の検査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/jezweb/claude-skills --skill accessibility

インストール後、Claude Code で/accessibilityを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
.claude-plugin/
plugin.json
agents/
a11y-auditor.md
references/
aria-patterns.md
color-contrast.md
focus-management.md
forms-validation.md
semantic-html.md
wcag-checklist.md
rules/
accessibility.md

詳細

Web アクセシビリティ(WCAG 2.1 AA)

ステータス: 本番環境で利用可能 ✅ 最終更新日: 2026-01-14 依存関係: なし(フレームワーク非依存) 標準: WCAG 2.1 レベル AA


クイックスタート(5 分)

1. セマンティック HTML の基礎

適切な要素を選びましょう。すべてに div を使わないでください。


<div onclick="submit()">Submit</div>
<div onclick="navigate()">Next page</div>


<button type="submit">Submit</button>
<a href="/next">Next page</a>

重要な理由:

  • セマンティック要素にはキーボード操作のサポートが組み込まれています
  • スクリーンリーダーはロールを自動的に読み上げます
  • ブラウザーは標準でアクセシブルな動作を提供します
2. フォーカス管理

操作可能な要素をキーボードでアクセスできるようにします。

/* ❌ WRONG - removes focus outline */
button:focus { outline: none; }

/* ✅ CORRECT - custom accessible outline */
button:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

重要:

  • 代替手段を用意せずにフォーカスアウトラインを削除しないでください
  • :focus-visible を使って、キーボードフォーカス時のみ表示します
  • フォーカスインジケーターのコントラスト比を 3:1 以上にします
3. テキストによる代替

テキスト以外のすべての要素には、テキストによる代替が必要です。


<img src="logo.png">
<button><svg>...</svg></button>


<img src="logo.png" alt="Company Name">
<button aria-label="Close dialog"><svg>...</svg></button>

アクセシビリティ対応の 5 ステップ

ステップ 1:セマンティック HTML を選ぶ

要素選択の判断ツリー:

Need clickable element?
├─ Navigates to another page? → <a href="...">
├─ Submits form? → <button type="submit">
├─ Opens dialog? → <button aria-haspopup="dialog">
└─ Other action? → <button type="button">

Grouping content?
├─ Self-contained article? → <article>
├─ Thematic section? → <section>
├─ Navigation links? → <nav>
└─ Supplementary info? → <aside>

Form element?
├─ Text input? → <input type="text">
├─ Multiple choice? → <select> or <input type="radio">
├─ Toggle? → <input type="checkbox"> or <button aria-pressed>
└─ Long text? → <textarea>

完全なガイドは references/semantic-html.md を参照してください。

ステップ 2:必要に応じて ARIA を追加する

基本原則:HTML でパターンを表現できない場合に限り、ARIA を使います。


<button role="button">Click me</button>  


<div role="dialog" aria-labelledby="title" aria-modal="true">
  <h2 id="title">Confirm action</h2>
  
</div>


<dialog aria-labelledby="title">
  <h2 id="title">Confirm action</h2>
</dialog>

よく使われる ARIA パターン:

  • aria-label - 表示ラベルがない場合
  • aria-labelledby - 既存のテキストをラベルとして参照する場合
  • aria-describedby - 追加の説明
  • aria-live - 動的な更新を読み上げる場合
  • aria-expanded - 折りたたみ/展開状態

パターンの詳細は references/aria-patterns.md を参照してください。

ステップ 3:キーボード操作を実装する

すべての操作可能な要素は、キーボードでアクセスできなければなりません。

// Tab order management
function Dialog({ onClose }) {
  const dialogRef = useRef<HTMLDivElement>(null);
  const previousFocus = useRef<HTMLElement | null>(null);

  useEffect(() => {
    // Save previous focus
    previousFocus.current = document.activeElement as HTMLElement;

    // Focus first element in dialog
    const firstFocusable = dialogRef.current?.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
    (firstFocusable as HTMLElement)?.focus();

    // Trap focus within dialog
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
      if (e.key === 'Tab') {
        // Focus trap logic here
      }
    };

    document.addEventListener('keydown', handleKeyDown);
    return () => {
      document.removeEventListener('keydown', handleKeyDown);
      // Restore focus on close
      previousFocus.current?.focus();
    };
  }, [onClose]);

  return <div ref={dialogRef} role="dialog">...</div>;
}

基本的なキーボード操作パターン:

  • Tab/Shift+Tab:フォーカス可能な要素間を移動する
  • Enter/Space:ボタン/リンクを実行する
  • 矢印キー:コンポーネント内(タブ、メニュー)を移動する
  • Escape:ダイアログ/メニューを閉じる
  • Home/End:先頭/末尾の項目に移動する

パターンの詳細は references/focus-management.md を参照してください。

ステップ 4:色のコントラストを確保する

WCAG AA の要件:

  • 通常のテキスト(18pt 未満):コントラスト比 4.5:1
  • 大きなテキスト(18pt 以上、または太字で 14pt 以上):コントラスト比 3:1
  • UI コンポーネント(ボタン、境界線):コントラスト比 3:1
/* ❌ WRONG - insufficient contrast */
:root {
  --background: #ffffff;
  --text: #999999;  /* 2.8:1 - fails WCAG AA */
}

/* ✅ CORRECT - sufficient contrast */
:root {
  --background: #ffffff;
  --text: #595959;  /* 4.6:1 - passes WCAG AA */
}

テストツール:

  • ブラウザーの開発者ツール(Chrome/Firefox にはチェック機能が組み込まれています)
  • コントラストチェッカーの拡張機能
  • axe DevTools 拡張機能

完全なガイドは references/color-contrast.md を参照してください。

ステップ 5:フォームをアクセシブルにする

すべてのフォーム入力には、表示されるラベルが必要です。


<input type="email" placeholder="Email address">


<label for="email">Email address</label>
<input type="email" id="email" name="email" required aria-required="true">

エラー処理:

<label for="email">Email address</label>
<input
  type="email"
  id="email"
  name="email"
  aria-invalid="true"
  aria-describedby="email-error"
>
<span id="email-error" role="alert">
  Please enter a valid email address
</span>

動的なエラーに対するライブリージョン:

<div role="alert" aria-live="assertive" aria-atomic="true">
  Form submission failed. Please fix the errors above.
</div>

パターンの詳細は references/forms-validation.md を参照してください。


重要なルール

必ず行うこと

✅ まずセマンティック HTML 要素(button、a、nav、article など)を使う ✅ テキスト以外のすべてのコンテンツに、テキストによる代替を用意する ✅ 通常のテキストは 4.5:1、大きなテキスト/UI は 3:1 のコントラスト比を確保する ✅ すべての機能をキーボードで操作できるようにする ✅ キーボードのみでテストする(マウスを取り外す) ✅ スクリーンリーダーでテストする(Windows では NVDA、Mac では VoiceOver) ✅ 適切な見出し階層を使う(h1 → h2 → h3。階層を飛ばさない) ✅ すべてのフォーム入力に、表示されるラベルを付ける ✅ フォーカスインジケーターを用意する(outline: none のみで済ませない) ✅ 動的コンテンツの更新には aria-live を使う

絶対にしないこと

❌ button の代わりに onClick を設定した div を使う ❌ 代替手段を用意せずにフォーカスアウトラインを削除する ❌ 情報を伝える手段として色だけを使う ❌ ラベルとしてプレースホルダーを使う ❌ 見出しレベルを飛ばす(h1 → h3) ❌ tabindex > 0 を使う(自然な順序が崩れます) ❌ セマンティック HTML で表現できる場合に ARIA を追加する ❌ ダイアログを閉じた後にフォーカスを戻し忘れる ❌ フォーカス可能な要素に role="presentation" を使う ❌ キーボードトラップを作る(抜け出す方法がない状態)


既知の問題の防止

このスキルは、文書化されているアクセシビリティの問題 12 件 を防止します。

問題 #1:フォーカスインジケーターがない

エラー:操作可能な要素に、視認できるフォーカスインジケーターがありません 出典:WCAG 2.4.7(フォーカスの可視化) 発生する理由:CSS リセットによってデフォルトのアウトラインが削除されます 防止策:常にカスタムの focus-visible スタイルを用意します

問題 #2:色のコントラストが不十分

エラー:テキストのコントラスト比が 4.5:1 未満です 出典:WCAG 1.4.3(コントラスト(最低限)) 発生する理由:白い背景に薄いグレーのテキストを使用しているため 防止策:コントラストチェッカーを使って、すべてのテキストの色を確認します

問題 #3:代替テキストがない

エラー:画像に alt 属性がありません 出典:WCAG 1.1.1(非テキストコンテンツ) 発生する理由:追加し忘れた、または任意だと思っていたため 防止策:装飾目的の画像には alt="" を、意味のある画像には内容を説明する alt を追加します

問題 #4:キーボード操作ができない

エラー:インタラクティブな要素にキーボードでフォーカスできません 出典:WCAG 2.1.1(キーボード) 発生する理由:button の代わりに div に onClick を使っているため 防止策:button や a など、セマンティックなインタラクティブ要素を使用します

問題 #5:フォーム入力欄にラベルがない

エラー:入力欄に対応するラベルがありません 出典:WCAG 3.3.2(ラベルまたは説明) 発生する理由:placeholder をラベルとして使っているため 防止策:必ず <label> 要素を使い、for/id で関連付けます

問題 #6:見出しレベルが飛んでいる

エラー:見出しの階層が h1 から h3 に飛んでいます 出典:WCAG 1.3.1(情報および関係性) 発生する理由:意味ではなく見た目を整えるために見出しを使っているため 防止策:見出しを順序どおりに使い、スタイルは CSS で設定します

問題 #7:ダイアログにフォーカストラップがない

エラー:Tab キーでダイアログから背後のコンテンツに移動できます 出典:WCAG 2.4.3(フォーカス順序) 発生する理由:フォーカストラップが実装されていないため 防止策:モーダルダイアログにフォーカストラップを実装します

問題 #8:動的コンテンツに aria-live がない

エラー:スクリーンリーダーが更新を通知しません 出典:WCAG 4.1.3(ステータスメッセージ) 発生する理由:通知なしで動的コンテンツが追加されているため 防止策:aria-live="polite" または "assertive" を使います

問題 #9:色だけで情報を伝えている

エラー:状態を色だけで伝えています 出典:WCAG 1.4.1(色の使用) 発生する理由:エラーを赤いテキストだけで示し、アイコンやテキストを併用していないため 防止策:色だけでなく、アイコンとテキストラベルを追加します

問題 #10:リンクテキストが不明確

エラー:「ここをクリック」や「続きを読む」というリンクがあります 出典:WCAG 2.4.4(リンクの目的) 発生する理由:文脈のない汎用的なリンクテキストを使っているため 防止策:内容がわかるリンクテキストまたは aria-label を使います

問題 #11:メディアが自動再生される

エラー:ユーザーが操作できない状態で動画や音声が自動再生されます 出典:WCAG 1.4.2(音声の制御) 発生する理由:controls を指定せずに autoplay 属性を使っているため 防止策:メディアの再生開始にユーザーの操作を必要とします

問題 #12:カスタムコントロールがアクセシブルでない

エラー:カスタムのセレクトボックスやチェックボックスをキーボードで操作できません 出典:WCAG 4.1.2(名前、役割、値) 発生する理由:ARIA を使わずに div で構築しているため 防止策:ネイティブ要素を使うか、ARIA パターンを完全に実装します


WCAG 2.1 AA クイックチェックリスト

知覚可能
  • すべての画像に代替テキストがある(装飾目的の場合は alt="")
  • テキストのコントラスト比が 4.5:1 以上(通常サイズ)、3:1 以上(大きな文字)
  • 情報を色だけで伝えていない
  • コンテンツを失うことなくテキストを 200% まで拡大できる
  • 3 秒を超える音声が自動再生されない
操作可能
  • すべての機能をキーボードで操作できる
  • キーボードトラップがない
  • フォーカスインジケーターが表示される
  • 動くコンテンツを一時停止、停止、または非表示にできる
  • ページタイトルで目的がわかる
  • フォーカス順序が論理的である
  • リンクの目的がテキストまたは文脈からわかる
  • ページを見つける方法が複数ある(メニュー、検索、サイトマップ)
  • 見出しとラベルで目的がわかる
理解可能
  • ページの言語が指定されている(<html lang="en">)
  • 言語の変更がマークされている(<span lang="es">)
  • フォーカス時や入力時に、予期しないコンテキストの変化がない
  • サイト全体でナビゲーションが一貫している
  • フォームのラベルや説明がある
  • 入力エラーが特定され、説明されている
  • 法的事項、財務、データの変更についてエラーを防止できる
堅牢
  • HTML が有効である(解析エラーがない)
  • すべての UI コンポーネントで名前、役割、値を取得できる
  • ステータスメッセージが識別されている(aria-live)

テストのワークフロー

1. キーボードのみでのテスト(5分)
1. Unplug mouse or hide cursor
2. Tab through entire page
   - Can you reach all interactive elements?
   - Can you activate all buttons/links?
   - Is focus order logical?
3. Use Enter/Space to activate
4. Use Escape to close dialogs
5. Use arrow keys in menus/tabs
2. スクリーンリーダーでのテスト(10分)

NVDA(Windows - 無料):

VoiceOver(Mac - 標準搭載):

  • 起動:Cmd+F5
  • 移動:VO+Right/Left arrow(VO = Ctrl+Option)
  • 読み上げ:VO+A(すべて読み上げ)
  • 停止:Cmd+F5

テスト項目:

  • すべてのインタラクティブ要素が読み上げられますか?
  • 画像の説明は適切ですか?
  • フォームのラベルは入力欄と一緒に読み上げられますか?
  • 動的な更新は読み上げられますか?
  • 見出しの構造は明確ですか?
3. 自動テスト

axe DevTools(ブラウザー拡張機能 - 強く推奨):

  • インストール:Chrome/Firefox 拡張機能
  • 実行:F12 → axe DevTools タブ → Scan
  • 修正:違反内容を確認し、修正方法に従います
  • 再テスト:修正後に再度スキャンします

Lighthouse(Chrome に標準搭載):

  • DevTools を開きます(F12)
  • Lighthouse タブを開きます
  • 「Accessibility」カテゴリを選択します
  • レポートを生成します
  • スコア90以上が良好で、100が理想です

よくあるパターン

パターン 1:アクセシブルなダイアログ/モーダル
interface DialogProps {
  isOpen: boolean;
  onClose: () => void;
  title: string;
  children: React.ReactNode;
}

function Dialog({ isOpen, onClose, title, children }: DialogProps) {
  const dialogRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!isOpen) return;

    const previousFocus = document.activeElement as HTMLElement;

    // Focus first focusable element
    const firstFocusable = dialogRef.current?.querySelector(
      'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
    ) as HTMLElement;
    firstFocusable?.focus();

    // Focus trap
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        onClose();
      }
      if (e.key === 'Tab') {
        const focusableElements = dialogRef.current?.querySelectorAll(
          'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
        );
        if (!focusableElements?.length) return;

        const first = focusableElements[0] as HTMLElement;
        const last = focusableElements[focusableElements.length - 1] as HTMLElement;

        if (e.shiftKey && document.activeElement === first) {
          e.preventDefault();
          last.focus();
        } else if (!e.shiftKey && document.activeElement === last) {
          e.preventDefault();
          first.focus();
        }
      }
    };

    document.addEventListener('keydown', handleKeyDown);

    return () => {
      document.removeEventListener('keydown', handleKeyDown);
      previousFocus?.focus();
    };
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return (
    <>
      {/* Backdrop */}
      <div
        className="dialog-backdrop"
        onClick={onClose}
        aria-hidden="true"
      />

      {/* Dialog */}
      <div
        ref={dialogRef}
        role="dialog"
        aria-modal="true"
        aria-labelledby="dialog-title"
        className="dialog"
      >
        <h2 id="dialog-title">{title}</h2>
        <div className="dialog-content">{children}</div>
        <button onClick={onClose} aria-label="Close dialog">×</button>
      </div>
    </>
  );
}

使用する場面:背後のコンテンツとの操作を妨げるモーダルダイアログやオーバーレイ全般。

パターン 2:アクセシブルなタブ
function Tabs({ tabs }: { tabs: Array<{ label: string; content: React.ReactNode }> }) {
  const [activeIndex, setActiveIndex] = useState(0);

  const handleKeyDown = (e: React.KeyboardEvent, index: number) => {
    if (e.key === 'ArrowLeft') {
      e.preventDefault();
      const newIndex = index === 0 ? tabs.length - 1 : index - 1;
      setActiveIndex(newIndex);
    } else if (e.key === 'ArrowRight') {
      e.preventDefault();
      const newIndex = index === tabs.length - 1 ? 0 : index + 1;
      setActiveIndex(newIndex);
    } else if (e.key === 'Home') {
      e.preventDefault();
      setActiveIndex(0);
    } else if (e.key === 'End') {
      e.preventDefault();
      setActiveIndex(tabs.length - 1);
    }
  };

  return (
    <div>
      <div role="tablist" aria-label="Content tabs">
        {tabs.map((tab, index) => (
          <button
            key={index}
            role="tab"
            aria-selected={activeIndex === index}
            aria-controls={`panel-${index}`}
            id={`tab-${index}`}
            tabIndex={activeIndex === index ? 0 : -1}
            onClick={() => setActiveIndex(index)}
            onKeyDown={(e) => handleKeyDown(e, index)}
          >
            {tab.label}
          </button>
        ))}
      </div>
      {tabs.map((tab, index) => (
        <div
          key={index}
          role="tabpanel"
          id={`panel-${index}`}
          aria-labelledby={`tab-${index}`}
          hidden={activeIndex !== index}
          tabIndex={0}
        >
          {tab.content}
        </div>
      ))}
    </div>
  );
}

使用する場面:複数のパネルがあるタブ付きインターフェース。

パターン 3:スキップリンク

<a href="#main-content" class="skip-link">
  Skip to main content
</a>

<style>
.skip-link {
  position: absolute;
  top: -40px;
  left: 0;
  background: var(--primary);
  color: white;
  padding: 8px 16px;
  z-index: 9999;
}

.skip-link:focus {
  top: 0;
}
</style>


<main id="main-content" tabindex="-1">
  
</main>

使用する場面:メインコンテンツの前にナビゲーションやヘッダーがある複数ページ構成のウェブサイト全般。

パターン 4:バリデーション機能付きのアクセシブルなフォーム
function ContactForm() {
  const [errors, setErrors] = useState<Record<string, string>>({});
  const [touched, setTouched] = useState<Record<string, boolean>>({});

  const validateEmail = (email: string) => {
    if (!email) return 'Email is required';
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return 'Email is invalid';
    return '';
  };

  const handleBlur = (field: string, value: string) => {
    setTouched(prev => ({ ...prev, [field]: true }));
    const error = validateEmail(value);
    setErrors(prev => ({ ...prev, [field]: error }));
  };

  return (
    <form>
      <div>
        <label htmlFor="email">Email address *</label>
        <input
          type="email"
          id="email"
          name="email"
          required
          aria-required="true"
          aria-invalid={touched.email && !!errors.email}
          aria-describedby={errors.email ? 'email-error' : undefined}
          onBlur={(e) => handleBlur('email', e.target.value)}
        />
        {touched.email && errors.email && (
          <span id="email-error" role="alert" className="error">
            {errors.email}
          </span>
        )}
      </div>

      <button type="submit">Submit</button>

      {/* Global form error */}
      <div role="alert" aria-live="assertive" aria-atomic="true">
        {/* Dynamic error message appears here */}
      </div>
    </form>
  );
}

使用する場面:バリデーション機能のあるフォーム全般。


同梱リソースの使用

リファレンス(references/)

詳細な調査に役立つドキュメント:

  • wcag-checklist.md - WCAG 2.1 レベル A および AA の要件を、例とともに網羅
  • semantic-html.md - 要素の選択ガイドと、各タグの使い分け
  • aria-patterns.md - ARIA のロール、状態、プロパティと使い方
  • focus-management.md - フォーカス順序、フォーカストラップ、フォーカス復帰のパターン
  • color-contrast.md - コントラスト要件、テストツール、カラーパレットのヒント
  • forms-validation.md - アクセシブルなフォームのパターン、エラー処理、読み上げ

Claude がこれらを読み込む場面:

  • ユーザーが WCAG チェックリストの全項目を求めている場合
  • 特定のパターン(タブ、アコーディオンなど)を詳しく調べる場合
  • 色のコントラストの問題やパレットの設計について扱う場合
  • 複雑なフォームのバリデーションシナリオを扱う場合
エージェント(agents/)
  • a11y-auditor.md - ページの違反をチェックする自動アクセシビリティ監査エージェント

使用する場面:既存のページ/コンポーネントのアクセシビリティ監査を依頼された場合。


高度なトピック

ARIA ライブリージョン

3つの通知レベル:


<div aria-live="polite">New messages: 3</div>


<div aria-live="assertive" role="alert">
  Error: Form submission failed
</div>


<div aria-live="off">Loading...</div>

ベストプラクティス:

  • 通知やカウンターなど、重要度の低い更新には polite を使用します
  • エラーや重大な警告には assertive を使用します
  • 変更時にリージョン全体を読み上げるには aria-atomic="true" を使用します
  • メッセージは簡潔で意味のあるものにします
SPA でのフォーカス管理

React Router はナビゲーション時にフォーカスをリセットしないため、手動で処理する必要があります:

function App() {
  const location = useLocation();
  const mainRef = useRef<HTMLElement>(null);

  useEffect(() => {
    // Focus main content on route change
    mainRef.current?.focus();
    // Announce page title to screen readers
    const title = document.title;
    const announcement = document.createElement('div');
    announcement.setAttribute('role', 'status');
    announcement.setAttribute('aria-live', 'polite');
    announcement.textContent = `Navigated to ${title}`;
    document.body.appendChild(announcement);
    setTimeout(() => announcement.remove(), 1000);
  }, [location.pathname]);

  return <main ref={mainRef} tabIndex={-1} id="main-content">...</main>;
}
アクセシブルなデータテーブル
<table>
  <caption>Monthly sales by region</caption>
  <thead>
    <tr>
      <th scope="col">Region</th>
      <th scope="col">Q1</th>
      <th scope="col">Q2</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">North</th>
      <td>$10,000</td>
      <td>$12,000</td>
    </tr>
  </tbody>
</table>

主な属性:

  • <caption> - テーブルの目的を説明します
  • scope="col" - 列見出しを識別します
  • scope="row" - 行見出しを識別します
  • スクリーンリーダーがデータセルと見出しを関連付けられるようにします

公式ドキュメント


トラブルシューティング

問題:フォーカスインジケーターが表示されない

症状:ページ内を Tab キーで移動できますが、フォーカス位置がわかりません 原因:CSS によってアウトラインが削除されているか、コントラストが不十分です 解決策:

*:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
問題:スクリーンリーダーが更新を読み上げない

症状:動的コンテンツが変化しても読み上げられません 原因:aria-live リージョンがありません 解決策:動的コンテンツを <div aria-live="polite"> で囲むか、role="alert" を使用します

問題:ダイアログから背景へフォーカスが移動する

症状:Tab キーを押すと、ダイアログの背後にある要素に移動します 原因:フォーカストラップがありません 解決策:フォーカストラップを実装します(上記のパターン 1 を参照してください)

問題:フォームのエラーが読み上げられない

症状:画面にはエラーが表示されますが、スクリーンリーダーが認識しません 原因:aria-invalid または role="alert" がありません 解決策:aria-invalid と aria-describedby を使用し、role="alert" を指定したエラーメッセージを参照します


セットアップの完全チェックリスト

すべてのページ/コンポーネントで使用してください。

  • すべてのインタラクティブ要素がキーボードで操作できる
  • すべてのフォーカス可能な要素に視認できるフォーカスインジケーターがある
  • 画像に alt テキストがある(装飾目的の場合は alt="")
  • テキストのコントラスト比が 4.5:1 以上である(axe または Lighthouse でテスト)
  • フォーム入力に関連付けられたラベルがある(プレースホルダーだけではない)
  • 見出しの階層が論理的である(レベルを飛ばしていない)
  • ページに <html lang="en"> または適切な言語が指定されている
  • ダイアログにフォーカストラップがあり、閉じたときにフォーカスが元に戻る
  • 動的コンテンツで aria-live または role="alert" を使用している
  • 情報を伝える手段として色だけに頼っていない
  • キーボードのみでテスト済み(マウスなし)
  • スクリーンリーダー(NVDA または VoiceOver)でテスト済み
  • axe DevTools のスキャンを実行済み(違反 0 件)
  • Lighthouse のアクセシビリティスコアが 90 以上

質問や問題がありますか?

  1. 完全な要件については references/wcag-checklist.md を確認してください
  2. /a11y-auditor エージェントを使用してページをスキャンしてください
  3. axe DevTools を実行して自動テストを行ってください
  4. 実際のキーボードとスクリーンリーダーでテストしてください

標準:WCAG 2.1 レベル AA テストツール:axe DevTools、Lighthouse、NVDA、VoiceOver 成功基準:Lighthouse スコア 90 以上、重大な違反 0 件

コメント