ARIA
複雑なコンポーネントのセマンティクスを豊かにしてアクセシブルにする
ARIAとは?
ARIA(Accessible Rich Internet Applications)は、リッチなWebアプリケーションと動的コンポーネントのアクセシビリティを向上させることができるW3C仕様です。ARIA (Accessible Rich Internet Applications)
🏷️ 役割
要素が何であるかを定義(ボタン、ナビゲーション、アラートなど)
role="button"📊 状態
要素の現在の状態を記述(チェック済み、展開済み、無効など)
aria-expanded="true"🔧 プロパティ
追加情報を提供(ラベル、describedby、コントロールなど)
aria-label="メインメニュー"⚠️ ARIAのゴールデンルール
ネイティブHTMLを使用できる場合はARIAを使用しない
ARIA属性を使用したフォーム
悪い例
ARIAなしのフォーム
❌ 適切なARIA属性がないアクセシブルでないフォーム:
<!-- 悪い例:ラベルなし、説明なし -->
<input type="text" placeholder="あなたのメール">
<!-- 悪い例:アイコンがアクセシブルでない -->
<button>
<span class="icon">🔍</span>
</button>
<!-- 悪い例:エラーが関連付けられていない、オートコンプリートなし -->
<input type="password">
<div class="error">パスワードが短すぎます</div>良い例
ARIAを使用したフォーム
✅ 適切なARIA属性を使用したアクセシブルなフォーム:
<!-- 良い例:可視ラベルのないフィールドのaria-label -->
<input
type="text"
aria-label="メールアドレス(必須)"
aria-describedby="email-desc"
aria-required="true">
<span id="email-desc">形式:name@example.com</span>
<!-- 良い例:aria-labelを持つボタン -->
<button aria-label="フォームを送信">
<span aria-hidden="true">🔍</span>
</button>
<!-- 良い例:aria-describedbyで関連付けられたエラー -->
<input
type="password"
aria-label="パスワード(8文字以上)"
aria-invalid="true"
aria-describedby="pwd-error"
autocomplete="new-password">
<div id="pwd-error" role="alert">
パスワードは8文字以上である必要があります
</div>使用されるARIA属性:
aria-label:アクセシブルなラベルを提供aria-describedby:詳細な説明を関連付けるaria-required:必須フィールドを示すaria-invalid:無効なフィールドを通知aria-labelledby:既存のラベルを参照role="alert":エラーをすぐにアナウンスaria-hidden:装飾的要素を非表示aria-live:動的変更をアナウンス
説明: ARIA属性は、支援技術にラベル、説明、状態を提供することでフォームを豊かにします。
アクセシブルなアコーディオン
悪い例
ARIA属性なし
<!-- 悪い例:ARIA属性なし、キーボードナビゲーションなし -->
<div class="accordion-header" @click="toggle">
<span>Title</span>
<span>+</span>
</div>
<div v-if="open">
Content
</div>良い例
適切なARIAを使用
<!-- 良い例:キーボードナビゲーション付きの完全なARIA -->
<button
id="accordion-button-1"
aria-expanded="false"
aria-controls="panel-1"
@click="toggle"
@keydown.enter="toggle"
>
Title
</button>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-button-1"
:hidden="!open"
>
Content
</div>説明: アコーディオンは、適切なARIA属性を使用し、キーボードナビゲーションをサポートし、状態の変更をアナウンスする必要があります。
Tabindexとキーボードナビゲーション
tabindex属性は、要素がキーボードフォーカスを受け取ることができるかどうか、どの順序で受け取るかを制御します。
✅ tabindex="0"
要素は自然なDOM順序でフォーカス可能
tabindex="0"<!-- 良い例:インタラクティブ要素がフォーカス可能 -->
<div role="button" aria-label="Custom action" tabindex="0">
Custom button
</div>⚠️ tabindex="-1"
JavaScriptでフォーカス可能だがTabではない
tabindex="-1"<!-- 良い例:プログラム的フォーカス用 -->
<div role="alert" aria-live="assertive" tabindex="-1">
Error message
</div>❌ tabindex > 0
ナビゲーション順序を変更(避けるべき!)
tabindex="5"<!-- 悪い例:自然なタブ順序を壊す -->
<button tabindex="3">Third</button>
<button tabindex="1">First</button>
<button tabindex="2">Second</button>📋 Tabindexのベストプラクティス
- カスタムインタラクティブ要素をフォーカス可能にするには
tabindex="0"を使用 - プログラム的フォーカス(モーダル、アラート)には
tabindex="-1"を使用 - 正の値は絶対に使用しない - DOM順序を尊重する
- ネイティブなインタラクティブ要素(
<button>、<a>、<input>)はtabindexを必要としない
🎯 インタラクティブな例
これらの要素でキーボードナビゲーション(Tab/Shift+Tab)をテストします:
❌ フォーカス不可能な要素
段落
💡 テストのヒント
- Tabを使用して前方にナビゲート
- Shift + Tabを使用して後方にナビゲート
- SpaceまたはEnterを使用して要素を有効化
- フォーカスインジケーター(青いアウトライン)を観察
- どの要素がフォーカスを受け取れるか、受け取れないかに注意
アクセシブルなドロップダウンメニュー
悪い例
ARIAなしのメニュー
<!-- 悪い例:メニュー役割なし、フォーカストラップなし -->
<div @click="open = !open">
Actions ▼
</div>
<div v-if="open">
<div @click="action">Edit</div>
<div @click="action">Delete</div>
</div>良い例
ARIAを使用したメニュー
<!-- 良い例:ARIA属性とフォーカス管理 -->
<button
aria-haspopup="true"
aria-expanded="false"
aria-controls="menu"
@keydown.escape="close"
>
Actions
</button>
<div
id="menu"
role="menu"
v-if="open"
>
<button role="menuitem">Edit</button>
<button role="menuitem">Delete</button>
</div>説明: ドロップダウンメニューは、キーボードナビゲーションをサポートし、Escapeで閉じ、適切にフォーカスを維持する必要があります。
アクセシブルな画像ギャラリー
悪い例
アクセシブルな画像ギャラリー
<!-- ❌ 悪い例:ギャラリーがキーボードでアクセスできない -->
<div class="thumbnails">
<img v-for="image in images"
:src="image.thumb"
@click="selectImage(index)"
:class="{ active: selected === index }" />
</div>
<div class="main-image">
<img :src="images[selected].full" />
</div>良い例
アクセシブルな画像ギャラリー
16インチMacBook Proの前面図、画面が閉じられ、洗練されたアルミニウムデザインを示しています。
<!-- ✅ 良い例:キーボードナビゲーション付きのアクセシブルなギャラリー -->
<div role="tablist" aria-label="製品画像">
<button v-for="(image, index) in images"
:id="`tab-${index}`"
role="tab"
:aria-selected="selected === index"
:aria-controls="`panel-${index}`"
:tabindex="selected === index ? 0 : -1"
@click="selectImage(index)"
@keydown="handleKeydown($event, index)">
<img :src="image.thumb" :alt="`${image.alt} - Miniature`" />
</button>
</div>
<div :id="`panel-${selected}`"
role="tabpanel"
:aria-labelledby="`tab-${selected}`">
<img :src="images[selected].full" :alt="images[selected].alt" />
<p>{{ images[selected].description }}</p>
</div>
<!-- Bonnes pratiques :
- Pattern tablist/tab/tabpanel ARIA
- Navigation avec flèches, Home, End
- Focus management avec tabindex
- Descriptions détaillées pour chaque image -->説明: 画像ギャラリーは、キーボードナビゲーションを許可し、各画像にコンテキスト説明を提供する必要があります。ARIA tablist/tab/tabpanelパターンは、この使用例に最適です。
アクセシブルなソート可能なテーブル
悪い例
| 名前 ↑ | メール | 役割 | ステータス |
|---|---|---|---|
| アリス・マーティン | alice@example.com | 管理者 | アクティブ |
| ダイアナ・モロー | diana@example.com | ユーザー | 停止中 |
| チャーリー・ルロイ | charlie@example.com | モデレーター | アクティブ |
| ボブ・デュポン | bob@example.com | ユーザー | 非アクティブ |
良い例
| アリス・マーティン | alice@example.com | 管理者 | アクティブ |
|---|---|---|---|
| ダイアナ・モロー | diana@example.com | ユーザー | 停止中 |
| チャーリー・ルロイ | charlie@example.com | モデレーター | アクティブ |
| ボブ・デュポン | bob@example.com | ユーザー | 非アクティブ |
説明: ソート可能なテーブルは、現在のソート状態を示し、キーボードナビゲーションを許可し、スクリーンリーダーに変更をアナウンスする必要があります。
動的なaria-live領域
悪い例
aria-liveなし
変更がアナウンスされない
<!-- Mauvais : pas d'attribut aria-live -->
<div class="status-banner">
<p></p>
</div>カウンター:0
問題点:
- 変更がアナウンスされない
- スクリーンリーダーユーザーはコンテンツが変更されたことを知らない
- メッセージの重要度の指示がない
良い例
適切なaria-liveを使用
変更が優先度に応じてアナウンスされる
<!-- Bon : avec aria-live="polite" pour informations -->
<div
class="status-banner"
aria-live="polite"
aria-atomic="true"
role="status"
>
<p>Information : Données mises à jour</p>
</div>
<!-- Bon : avec aria-live="assertive" pour alertes -->
<div
class="alert-banner"
aria-live="assertive"
aria-atomic="true"
role="alert"
>
<p>Attention : Action requise immédiatement</p>
</div>Politeゾーン(情報)
Assertiveゾーン(アラート)
利点:
- 重要なメッセージはaria-live="assertive"で中断
- 情報メッセージはaria-live="polite"で待機
- aria-atomic="true"は領域コンテンツ全体をアナウンス
- 適切な役割(status、alert)がより多くのコンテキストを提供
説明: Aria-live領域を使用すると、スクリーンリーダーにコンテンツの変更をアナウンスできます。'polite'は現在のアナウンスが終了するのを待ち、'assertive'はすぐに中断します。
アクセシブルなアイコンボタン
悪い例
良い例
説明: アイコンのみを含むボタンには、スクリーンリーダーが理解できるようにaria-labelまたは視覚的に隠されたテキストが必要です。
コンボボックス / 候補付き検索
悪い例
ARIAまたはキーボードナビゲーションなし
良い例
ARIAとキーボードナビゲーションを使用
説明: 候補付き検索(コンボボックス)は、キーボードでナビゲート可能で、結果をアナウンスし、選択を許可する必要があります。
トースト通知(Live Regions)
悪い例
aria-liveなし
❌ 通知がスクリーンリーダーにアナウンスされない
<!-- 悪い例:スクリーンリーダーにアナウンスされない -->
<div v-if="showToast" class="toast">
Votre demande a été enregistrée
</div>良い例
適切なaria-liveを使用
✅ 通知が適切にアナウンスされる
<!-- 良い例:情報通知(polite) -->
<div
v-if="showToast"
role="status"
aria-live="polite"
aria-atomic="true"
class="toast"
>
<p>Votre demande a été enregistrée</p>
<button @click="dismiss" aria-label="Fermer notification">✕</button>
</div>
<!-- 良い例:エラーアラート(assertive) -->
<div
v-if="showError"
role="alert"
aria-live="assertive"
aria-atomic="true"
class="toast error"
>
<p>Erreur : action impossible à exécuter</p>
<button @click="dismiss" aria-label="Fermer alerte">✕</button>
</div>違い:
role="status"+aria-live="polite":情報通知用、現在の読み上げを中断しないrole="alert"+aria-live="assertive":重大なエラー用、すぐに中断aria-atomic="true":通知コンテンツ全体をアナウンス
説明: 動的通知は、フォーカスを奪うことなくスクリーンリーダーにアナウンスされる必要があります。情報にはrole='status'を、エラーにはrole='alert'を使用します。