モーダルダイアログが開きました。Tabキーを押すと——フォーカスはモーダルの外、背景のコンテンツに移動してしまいます。スクリーンリーダーユーザーは「今どこにいるのか」が完全にわからなくなります。
よくある失敗パターンは「動的なUI変化にフォーカスを追従させない」ことです。モーダルを開いてもフォーカスが移動しない。モーダルを閉じてもフォーカスが元の場所に戻らない。SPAでページ遷移してもフォーカスがページ先頭に移動しない——これらはすべて、フォーカス管理の失敗です。
フォーカス管理が不適切なUIは、キーボードユーザーとスクリーンリーダーユーザーを「迷子」にします。視覚的にはUIが変化しているのに、フォーカスが追従していないため、ユーザーは「今何が起きているのか」「次にどこを操作すればいいのか」がわかりません。
1. 原則の定義
フォーカス管理(Focus Management)とは、モーダル・ドロップダウン・ページ遷移・動的コンテンツ更新などのUI変化に合わせて、キーボードフォーカスを適切な要素に移動・制御し、ユーザーが常に「現在地」を把握できるよう設計する原則。
本質は「UIの変化とフォーカスを同期させること」です。視覚的なUIの変化はマウスユーザーには目で見えます。しかしキーボードユーザー・スクリーンリーダーユーザーにとっては、フォーカスが移動しなければ「変化が起きた」ことすら認識できません。フォーカスはキーボードユーザーの「カーソル」であり、常にUIの状態と同期している必要があります。
2. いつ使うか(適用場面)
特に重要になるケース
- モーダル・ダイアログの開閉: 開いたとき→モーダル内の最初のフォーカス可能な要素にフォーカスを移動。閉じたとき→モーダルを開いたトリガー要素にフォーカスを戻す。
- フォーカストラップ: モーダルが開いている間、Tabキーがモーダル内でのみ循環し、背景のコンテンツにフォーカスが移動しないようにする。
- SPAのページ遷移: ページ遷移後、フォーカスをページ先頭の見出し(
<h1>)またはメインコンテンツの先頭に移動し、スクリーンリーダーがページ変化を認識できるようにする。 - 動的コンテンツ更新: フォームのバリデーションエラー・成功メッセージなど、動的に追加されたコンテンツを
aria-liveでスクリーンリーダーに通知する。
トレードオフが起きる場面
- 自動フォーカスの煩わしさ: ページ読み込み時に自動でフォーカスを移動させると、スクリーンリーダーユーザーには便利だが、マウスユーザーには予期しないスクロールが発生することがある。
- 実装の複雑さ: フォーカストラップ・フォーカス復元は、JavaScriptで実装する必要があり、ネストしたモーダルなど複雑なUIでは実装が難しい。
3. なぜ重要か(設計判断の核)
フォーカスはキーボードユーザーの「カーソル」だ。UIが変化するたびに、カーソルも追従しなければならない。
設計判断の基準
- 「このUI変化(モーダル開閉・ページ遷移)の後、フォーカスはどこにあるべきか?」→ 答えられなければ、フォーカス管理が設計されていない。
- 「モーダルを閉じた後、ユーザーは元の場所に戻れるか?」→ 戻れなければ、フォーカス復元を実装する。
優先順位の考え方
- 1. モーダルのフォーカストラップ(最優先): モーダルが開いている間、フォーカスがモーダル外に逃げないようにする。
- 2. モーダル閉じた後のフォーカス復元: 閉じるボタンを押した後、モーダルを開いたトリガー要素にフォーカスを戻す。
- 3. 動的コンテンツの
aria-live通知: エラーメッセージ・成功通知など、動的に追加されたコンテンツをスクリーンリーダーに通知する。例外条件
- ページ読み込み時の自動フォーカスは、コンテキストによって適切な場所が異なる。検索ページなら検索フィールド、記事ページなら見出しなど、ユーザーの目的に合わせて判断する。
4. 具体の設計ルール(チェックリスト)
最低ライン(Must - これ守らないと危険)
これがないと、キーボードユーザーはモーダルや動的UIで迷子になります。
理想ライン(Better - できると強い/プロの品質)
「使える」を超えて「スクリーンリーダーでも快適」な状態です。
5. UI例
同じ「モーダルダイアログ」でも、フォーカス管理の設計でキーボードユーザーの体験は大きく変わります。
改善プロセス
- モーダルを開いたときにフォーカスを移動する: JavaScriptで
modalElement.focus()またはfirstFocusableElement.focus()を呼び出し、モーダルが開いた瞬間にフォーカスをモーダル内に移動する。 - フォーカストラップを実装する: モーダルが開いている間、Tabキーのイベントをインターセプトし、フォーカスがモーダル内の最初と最後の要素の間でのみ循環するよう制御する。
- モーダルを閉じたときにフォーカスを復元する: モーダルを開いたトリガー要素への参照を保持し、閉じるときに
triggerElement.focus()を呼び出してフォーカスを戻す。
6. 関連リンク
- 関連リファレンス(理論): 変化盲 (Change Blindness), フィードバック (Feedback), 選択的注意 (Selective Attention)
- 用語集(定義): アクセシビリティ, WCAG, インクルーシブデザイン
- 関連するUI原則(横): キーボード操作 (Keyboard Navigation), 代替テキスト・ARIA (Alt Text & ARIA), コントラストと可読性 (Contrast & Readability)
7. まとめ
今日から直せる一手 担当しているサービスのモーダルを開き、Tabキーを押してみてください。フォーカスがモーダルの外に逃げるなら、フォーカストラップが実装されていません。まずこの一点を修正してください。
チームに共有するなら一言 「モーダルを開いたとき、フォーカスはどこへ行く?この問いに答えられないUIは、キーボードユーザーを迷子にしている。」
