mcpField
W
wcag

wcag

github.com/claude-dev-suiteデザイン・UI4640 回インストール2 日前に更新
リポジトリ

wcag について

WCAG 2.2 のアクセシビリティ原則、達成基準、実装方法をまとめたリファレンススキルです。Web コンテンツやインターフェースを評価するデザイナーと開発者向けに、設計、実装、手動・自動テストを扱います。

主な機能

  1. 01WCAG 2.2 の原則と達成基準
  2. 02知覚・操作・理解・堅牢性の指針
  3. 03キーボード操作とフォーカスの実践
  4. 04アクセシブルなフォームとエラー対応
  5. 05自動・手動テストのチェックリスト

活用シーン

  1. 01アクセシビリティ基準に沿った UI デザインの確認
  2. 02キーボード操作とフォーカス管理の改善
  3. 03フォームのラベルとエラー表示の確認
  4. 04手動・自動アクセシビリティ監査の計画

MCP 対応

包括的な WCAG ガイダンスを得るため、`mcp__documentation__fetch_docs` を案内しています。

インストール

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

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill wcag

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

詳細

WCAG 2.2 アクセシビリティ

深い知識:包括的な WCAG ガイドライン、適合基準、達成方法については、テクノロジーに wcag を指定して mcp__documentation__fetch_docs を使用してください。

このスキルを使用しない場合

  • 自動テストのセットアップ - axe テストツールの統合には axe-core スキルを使用してください
  • コンポーネントライブラリのアクセシビリティ - アクセシブルなコンポーネントパターンについては、React や Vue などのフレームワーク固有のスキルを使用してください
  • デザインシステム - 既製のアクセシブルなコンポーネントについては、UI ライブラリのスキルを使用してください
  • ARIA の実装のみ - このスキルは、ARIA だけでなく、より広範な WCAG 準拠を扱います

公式リファレンス

リソースURL
WCAG 2.2 仕様https://www.w3.org/TR/WCAG22/
クイックリファレンスhttps://www.w3.org/WAI/WCAG22/quickref/
WCAG の理解https://www.w3.org/WAI/WCAG22/Understanding/
axe-corehttps://github.com/dequelabs/axe-core
WAI-ARIAhttps://www.w3.org/TR/wai-aria-1.2/

適合レベル

レベル説明法的要件
A最低限のアクセシビリティ十分とされることはまれ
AA標準的なアクセシビリティほとんどの規制(ADA、EN 301 549)
AAA強化されたアクセシビリティ特殊な状況
WCAG 2.2 達成基準の数
レベル2.2 での新規項目合計
A030
AA624
AAA331

POUR の原則

1. 知覚可能
ガイドライン主な基準レベル
1.1 テキストによる代替すべての非テキストコンテンツにテキストによる代替があるA
1.2 時間依存メディアキャプション、音声解説A-AAA
1.3 適応可能コンテンツの構造、意味のある順序A
1.4 判別可能色のコントラスト、テキストの拡大、間隔A-AAA
// Text alternatives
<img src="chart.png" alt="Q3 sales increased 25% compared to Q2" />

// Decorative images
<img src="divider.png" alt="" role="presentation" />

// Color contrast (4.5:1 for normal text, 3:1 for large text)
// Use tools: WebAIM Contrast Checker, axe DevTools
2. 操作可能
ガイドライン主な基準レベル
2.1 キーボードすべての機能をキーボードで操作できるA
2.2 十分な時間時間制限を調整できるA-AAA
2.4 ナビゲーション可能スキップリンク、フォーカス順序、フォーカスの可視化A-AAA
2.5 入力モダリティターゲットサイズ、動作の代替手段A-AAA
// Skip link
<a href="#main-content" className="skip-link">
  Skip to main content
</a>

// Focus visible (2.4.7)
button:focus {
  outline: 3px solid #005fcc;
  outline-offset: 2px;
}

// Target size minimum (2.5.8) - 24x24 CSS pixels
.button {
  min-width: 44px;
  min-height: 44px;
  padding: 12px 16px;
}
3. 理解可能
ガイドライン主な基準レベル
3.1 読みやすいページの言語、一般的でない単語A-AAA
3.2 予測可能一貫したナビゲーション、識別A-AA
3.3 入力支援エラーの特定、ラベルA-AAA
// Language of page (3.1.1)
<html lang="en">

// Error identification (3.3.1)
<div role="alert" aria-live="assertive">
  Email is required and must be valid
</div>

// Labels (3.3.2)
<label htmlFor="email">Email Address</label>
<input id="email" type="email" aria-describedby="email-hint" />
<span id="email-hint">We'll never share your email</span>
4. 堅牢
ガイドライン主な基準レベル
4.1 互換性有効な HTML、名前/役割/値A
// Name, Role, Value (4.1.2)
<button aria-pressed="true" aria-label="Favorite this item">
  ★
</button>

// Custom controls
<div
  role="slider"
  aria-valuemin={0}
  aria-valuemax={100}
  aria-valuenow={50}
  aria-label="Volume"
  tabIndex={0}
/>

WCAG 2.2 の新規項目

レベル AA(必須)
基準説明
2.4.11 フォーカスが隠されない(最低限)フォーカスされた要素が少なくとも部分的に見える
2.4.13 フォーカスの外観フォーカスインジケーターがサイズとコントラストの要件を満たす
2.5.7 ドラッグ操作ドラッグに代わる単一ポインター操作を用意する
2.5.8 ターゲットサイズ(最低限)最低 24x24 CSS ピクセル
3.2.6 一貫したヘルプ一貫した場所にヘルプを表示する
3.3.7 冗長な入力情報の再入力を求めない
レベル AAA
基準説明
2.4.12 フォーカスが隠されない(強化)フォーカスされた要素が完全に見える
3.3.8 アクセシブルな認証(最低限)認知機能テストを行わない
3.3.9 アクセシブルな認証(強化)オブジェクトやコンテンツの認識を求めない

一般的なパターン

モーダルダイアログ
function Modal({ isOpen, onClose, title, children }) {
  const modalRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (isOpen) {
      // Trap focus inside modal
      modalRef.current?.focus();

      // Prevent body scroll
      document.body.style.overflow = 'hidden';
    }
    return () => {
      document.body.style.overflow = '';
    };
  }, [isOpen]);

  if (!isOpen) return null;

  return (
    <div
      role="dialog"
      aria-modal="true"
      aria-labelledby="modal-title"
      ref={modalRef}
      tabIndex={-1}
      onKeyDown={(e) => e.key === 'Escape' && onClose()}
    >
      <h2 id="modal-title">{title}</h2>
      {children}
      <button onClick={onClose}>Close</button>
    </div>
  );
}
ドロップダウンメニュー
function Dropdown({ label, items }) {
  const [isOpen, setIsOpen] = useState(false);
  const [activeIndex, setActiveIndex] = useState(-1);

  const handleKeyDown = (e: KeyboardEvent) => {
    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setActiveIndex(i => Math.min(i + 1, items.length - 1));
        break;
      case 'ArrowUp':
        e.preventDefault();
        setActiveIndex(i => Math.max(i - 1, 0));
        break;
      case 'Escape':
        setIsOpen(false);
        break;
      case 'Enter':
      case ' ':
        if (activeIndex >= 0) items[activeIndex].onClick();
        break;
    }
  };

  return (
    <div onKeyDown={handleKeyDown}>
      <button
        aria-haspopup="menu"
        aria-expanded={isOpen}
        onClick={() => setIsOpen(!isOpen)}
      >
        {label}
      </button>
      {isOpen && (
        <ul role="menu">
          {items.map((item, i) => (
            <li
              key={item.id}
              role="menuitem"
              tabIndex={activeIndex === i ? 0 : -1}
              aria-current={activeIndex === i}
            >
              {item.label}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}
バリデーション付きフォーム
function Form() {
  const [errors, setErrors] = useState<Record<string, string>>({});

  return (
    <form aria-describedby="form-errors">
      {Object.keys(errors).length > 0 && (
        <div id="form-errors" role="alert" aria-live="polite">
          <h2>Please fix the following errors:</h2>
          <ul>
            {Object.entries(errors).map(([field, msg]) => (
              <li key={field}>
                <a href={`#${field}`}>{msg}</a>
              </li>
            ))}
          </ul>
        </div>
      )}

      <div>
        <label htmlFor="email">
          Email <span aria-hidden="true">*</span>
          <span className="sr-only">(required)</span>
        </label>
        <input
          id="email"
          type="email"
          aria-required="true"
          aria-invalid={!!errors.email}
          aria-describedby={errors.email ? 'email-error' : undefined}
        />
        {errors.email && (
          <span id="email-error" role="alert">{errors.email}</span>
        )}
      </div>
    </form>
  );
}

テスト

axe-core による自動テスト
// Playwright + axe
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';

test('homepage has no accessibility violations', async ({ page }) => {
  await page.goto('/');

  const results = await new AxeBuilder({ page })
    .withTags(['wcag2a', 'wcag2aa', 'wcag22aa'])
    .analyze();

  expect(results.violations).toEqual([]);
});

// Vitest + axe
import { axe, toHaveNoViolations } from 'jest-axe';
import { render } from '@testing-library/react';

expect.extend(toHaveNoViolations);

test('Button is accessible', async () => {
  const { container } = render(<Button>Click me</Button>);
  const results = await axe(container);
  expect(results).toHaveNoViolations();
});
手動テストのチェックリスト
## Keyboard Navigation
- [ ] Tab through all interactive elements
- [ ] Shift+Tab navigates backwards
- [ ] Enter/Space activates buttons and links
- [ ] Arrow keys work in menus, tabs, sliders
- [ ] Escape closes modals and dropdowns
- [ ] No keyboard traps

## Screen Reader
- [ ] All images have alt text
- [ ] Form fields have labels
- [ ] Headings are hierarchical (h1 > h2 > h3)
- [ ] Links are descriptive (not "click here")
- [ ] Dynamic content announced (aria-live)

## Visual
- [ ] Color contrast meets 4.5:1 (normal text)
- [ ] Color contrast meets 3:1 (large text, UI components)
- [ ] Focus indicators visible
- [ ] Content readable at 200% zoom
- [ ] No horizontal scrolling at 320px width
ツール
ツール目的
axe DevTools監査用のブラウザー拡張機能
WAVE視覚的なアクセシビリティ評価
Lighthouseパフォーマンスとアクセシビリティの監査
NVDA/VoiceOverスクリーンリーダーのテスト
Color Contrast Analyzerコントラストのチェック

CSS ユーティリティ

/* Visually hidden but accessible to screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Focus visible only for keyboard users */
:focus:not(:focus-visible) {
  outline: none;
}

:focus-visible {
  outline: 3px solid #005fcc;
  outline-offset: 2px;
}

/* 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;
  }
}

/* High contrast mode support */
@media (forced-colors: active) {
  .button {
    border: 2px solid currentColor;
  }
}

チェックリスト

設計フェーズ
  • 色のコントラストが要件を満たしている
  • タッチターゲットが最小 44x44(推奨)
  • フォーカス状態が設計されている
  • エラー状態が色だけでなくテキストも含んでいる
開発
  • セマンティック HTML を使用している
  • HTML では不十分な場合にのみ ARIA を使用している
  • キーボードナビゲーションが機能する
  • SPA のフォーカス管理ができている
  • フォームのエラーがアクセシブルである
テスト
  • CI パイプラインに axe-core を導入している
  • スクリーンリーダーを手動でテストしている
  • キーボードのみのナビゲーションをテストしている
  • 200% のズームをテストしている

アンチパターン

アンチパターン問題点正しい方法
role のない <div> で aria-label を使用するロールのない非セマンティック要素上の ARIA は無視されますまずセマンティック HTML を使用します。<div role="button"> の代わりに <button> を使用します
ボタンに <div onclick=""> を使用するデフォルトではキーボードでアクセスできません適切なイベントハンドラーを設定した <button> を使用します
色だけのインジケーター色覚異常のあるユーザーには機能しません色とともにアイコン、テキスト、またはパターンを追加します
ラベルの代わりに placeholder を使用する入力時に消え、コントラストも低くなります常に for 属性を持つ <label> を使用します
tabindex > 0自然なタブ順序が乱れますtabindex="0" を使用するか、DOM の順序に従います
情報を伝える画像に alt="" を使用するスクリーンリーダーが重要なコンテンツを読み飛ばします説明的な alt テキストを指定します
フォーカスインジケーターがないユーザーが現在の位置を確認できません常に :focus-visible スタイルを表示します
メディアの自動再生スクリーンリーダーのユーザーを混乱させますユーザーの操作でメディアを開始するようにします

クイックトラブルシューティング

問題原因解決策
スクリーンリーダーがボタンを読み上げないアクセシブルな名前がありませんaria-label を追加するか、ボタン内に表示テキストを配置します
モーダルでキーボードトラップが発生するフォーカスが適切に管理されていません最初と最後の要素を使ったフォーカストラップを実装します
フォームのエラーが読み上げられないrole="alert" または aria-live がありませんエラーコンテナーに aria-live="assertive" を追加します
コントラストが低いという警告が出るテキストが 4.5:1 の比率を満たしていませんコントラストチェッカーを使用し、色を調整するかフォントサイズを大きくします
タブ順序がおかしいDOM の順序が視覚的な順序と一致していませんDOM の順序を視覚的なレイアウトに合わせ、tabindex > 0 は避けます
カスタムドロップダウンがアクセシブルでないARIA ロールやキーボード操作が不足していますrole="combobox"、aria-expanded、矢印キーによるナビゲーションを使用します
動的コンテンツが読み上げられない変更が通知されずに発生していますaria-live="polite" または role="status" を使用します
画像ボタンにラベルがないアイコンのみで、テキストによる代替がありません説明的なテキストを含む aria-label を追加します

関連スキル

コメント