モーダルでよくあるアクセシビリティの問題(と解決方法)

モーダルダイアログは現代のWebアプリケーションにおいて至る所で使われていますが、同時にアクセシビリティ障害の最も一般的な原因の一つでもあります。モーダルとは、メインコンテンツの上に表示され、続行する前にユーザーの操作を必要とするダイアログボックスです。実装が不適切な場合、モーダルはキーボードユーザーを閉じ込め、スクリーンリーダーを混乱させ、支援技術に依存するすべての人にとって不満の多い体験を生み出す可能性があります。モーダルで最も頻繁に発生するアクセシビリティの問題とその実用的な解決策を検証してみましょう。
重要なポイント
- フォーカス管理が重要:モーダルが開いたときにフォーカスをモーダルに移動し、閉じたときにトリガー要素に戻す
- role=“dialog”、aria-modal=“true”、アクセシブルなラベルを含む適切なARIA属性を使用する
- Tabサイクリングとエスケープキーサポートによる完全なキーボードナビゲーションを実装する
- モーダルが開いている間、背景コンテンツを完全に非活性化する
- 自動化ツールだけでなく、実際の支援技術でテストする
フォーカス管理の重要な役割
モーダルで最も深刻なアクセシビリティ問題は、壊れたフォーカス管理です。モーダルが開いたとき、フォーカスは即座にモーダル自体に移動する必要があります。通常は最初のインタラクティブ要素またはモーダルコンテナです。閉じるときは、それをトリガーした要素にフォーカスを戻さなければなりません。
よくある間違い: フォーカスが背景コンテンツに残り、ユーザーがモーダルの後ろの要素をタブで移動できてしまう。
解決策: 適切なフォーカス管理を実装する:
// モーダルを開くとき
const triggerElement = document.activeElement;
modal.showModal(); // またはカスタム実装の場合はmodal.focus()
// モーダルを閉じるとき
modal.close();
triggerElement.focus();
focus-trap-reactを使用するReactアプリケーションの場合:
import FocusTrap from 'focus-trap-react';
function Modal({ isOpen, onClose, children }) {
return (
<FocusTrap active={isOpen}>
<div role="dialog" aria-modal="true">
{children}
</div>
</FocusTrap>
);
}
欠落または不正なARIA属性
スクリーンリーダーは、モーダルダイアログを適切にアナウンスするために明示的な情報を必要とします。ARIA属性が欠落していたり誤用されていると、ユーザーはモーダルの目的と状態について推測することになります。
よくある間違い:
role="dialog"
またはrole="alertdialog"
がないaria-modal="true"
が欠落しているaria-label
またはaria-labelledby
によるアクセシブルな名前がない
解決策: 適切なARIA属性を使用する:
<!-- ネイティブdialog要素を使用(推奨) -->
<dialog aria-labelledby="modal-title" aria-describedby="modal-desc">
<h2 id="modal-title">削除の確認</h2>
<p id="modal-desc">この操作は元に戻すことができません。</p>
</dialog>
<!-- ARIAを使用したdiv -->
<div role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
aria-describedby="modal-desc">
<!-- モーダルコンテンツ -->
</div>
即座のユーザー応答を必要とする重要なアラートにはrole="alertdialog"
を使用してください。これにより、より積極的なスクリーンリーダーのアナウンスがトリガーされます。
壊れたキーボードナビゲーション
すべてのモーダルは完全にキーボードアクセシブルでなければなりません。ユーザーはTab/Shift+Tabでインタラクティブ要素を移動し、エスケープキーでモーダルを閉じることができる必要があります。
よくある間違い:
- エスケープキーサポートがない
- フォーカスがモーダルから逃げることができる(フォーカストラップがない)
- タブ順序が視覚的なレイアウトと一致しない
解決策: 完全なフォーカストラップを実装する:
function trapFocus(modalElement) {
const focusableElements = modalElement.querySelectorAll(
'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])'
);
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
modalElement.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeModal();
return;
}
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
});
firstElement.focus();
}
Discover how at OpenReplay.com.
背景コンテンツがインタラクティブなまま残る
モーダルが開いているとき、背景コンテンツは完全に非活性化されている必要があります。ユーザーはモーダルの後ろにある何とも相互作用できないはずです。
よくある間違い: 背景がスクロール可能またはキーボードナビゲーションでインタラクティブなまま残る。
解決策: 背景コンテンツを非活性化する:
// モーダルを開くとき
document.body.style.overflow = 'hidden';
document.querySelector('main').setAttribute('aria-hidden', 'true');
document.querySelector('main').setAttribute('inert', ''); // 現代的なアプローチ
// モーダルを閉じるとき
document.body.style.overflow = '';
document.querySelector('main').removeAttribute('aria-hidden');
document.querySelector('main').removeAttribute('inert');
モーダルのアクセシビリティテスト
自動化ツールはいくつかの問題を捉えますが、手動テストは依然として不可欠です:
-
キーボードテスト:
- モーダルを開き、フォーカスがそこに移動することを確認
- すべてのインタラクティブ要素をタブで移動
- タブがモーダル内でサイクルすることを確認
- エスケープを押して閉じる
- フォーカスがトリガー要素に戻ることを確認
-
スクリーンリーダーテスト:
- NVDA(Windows)またはVoiceOver(macOS)でテスト
- モーダルロールがアナウンスされることを確認
- タイトルと説明が読み上げられることを確認
- 背景コンテンツに到達できないことを確認
-
視覚的テスト:
- フォーカスインジケーターが見えることを確認
- 色のコントラストをチェック(WCAG AAは通常のテキストで4.5:1、大きなテキストとUIコンポーネントで3:1を要求)
- 閉じるボタンが明確にラベル付けされていることを確認
実装のベストプラクティス
可能な限りネイティブの<dialog>
要素を使用してください。 これは組み込みのフォーカス管理とARIAセマンティクスを提供します:
const dialog = document.querySelector('dialog');
dialog.showModal(); // 適切なフォーカストラップで開く
dialog.close(); // 閉じてフォーカスを戻す
カスタム実装の場合、このチェックリストに従ってください:
role="dialog"
とaria-modal="true"
を設定aria-labelledby
またはaria-label
でアクセシブルな名前を提供- 完全なキーボードサポート(Tab、Shift+Tab、エスケープ)を実装
- 堅牢なフォーカストラップを作成
- 背景のスクロールと相互作用を無効化
- 閉じるときにトリガー要素にフォーカスを戻す
- 見えるフォーカスインジケーターを含める
- 実際の支援技術でテスト
結論
アクセシブルなモーダルは単なるコンプライアンスの問題ではありません。すべてのユーザーにとってより良い体験を作り出します。これらの一般的な問題に対処することで、キーボードユーザー、スクリーンリーダーユーザー、そしてその間のすべての人にとってモーダルがシームレスに機能することを保証できます。セマンティックHTMLから始めて、適切なARIA属性を追加し、徹底的なフォーカス管理を実装し、常に実際の支援技術でテストしてください。
覚えておいてください:マウスなしで機能しないモーダルは、機能していないのです。これらの問題を修正すれば、あなたのモーダルは真にすべての人にアクセシブルになります。
よくある質問
フォームや情報を含む標準的なモーダルにはrole='dialog'を使用してください。即座の応答を必要とする重要なアラートにはrole='alertdialog'を使用してください。これによりスクリーンリーダーがより積極的にコンテンツをアナウンスし、ユーザーの現在のタスクを中断します。
CSSはオーバーレイでコンテンツを視覚的に隠し、ポインターイベントを防ぐことができますが、キーボードナビゲーションを停止することはできません。すべてのユーザーにとって背景コンテンツを真に非インタラクティブにするには、aria-hiddenまたはinert属性を追加するJavaScriptが必要です。
ベストプラクティスは、最初のインタラクティブ要素、通常は上部にある場合は閉じるボタンにフォーカスすることです。ただし、重要な情報を含むモーダルの場合、最初に見出しにフォーカスすることで、スクリーンリーダーユーザーがアクションの前にタイトルを聞くことができます。
Truly understand users experience
See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..