通知・ダイアログ

モーダルダイアログ

解説あり

Dialog (Modal)

背面を操作不能にして前面に出すダイアログ。フォーカストラップと Esc 閉じが要。

モーダルダイアログとは?

モーダルダイアログは、画面の上に重なって開き、閉じるまで背面の操作をブロックする小さなウィンドウです。 設定パネル、フォーム、画像の拡大表示などに使われます。

モーダルで一番むずかしいのはフォーカスの管理です。 幸い、最近のブラウザのネイティブ <dialog> 要素をshowModal() で開けば、その大変な部分が標準で手に入ります

なぜアクセシビリティが大事なの?

モーダルが正しくないと、こんな問題が起きます。

ネイティブ <dialog> + showModal() は、これらすべて (フォーカストラップ・Escで閉じる・背面を inert 化・フォーカスの復帰)を 自動でやってくれます。

ライブデモ(推奨実装)

下の「設定を開く」を押すとダイアログが開きます。キーボードだけで操作して、Tab が外に漏れないこと・Esc で閉じることを確かめてください。

ネイティブ <dialog> + showModal() のモーダル

表示設定

テーマやフォントサイズをここで変更できます。(デモ用の中身です)

試してみよう:Enter で開く → Tab を連打してもフォーカスはダイアログ内をループ → Esc で閉じる → フォーカスが「設定を開く」ボタンに戻る。

ポイント

開いた瞬間、フォーカスはダイアログ内(最初のボタンや autofocus を付けた要素)へ移ります。 閉じると開いたトリガーへ自動で戻るので、利用者は元の文脈を失いません。

キーボード操作

キー動作必須/任意
Escダイアログを閉じる(showModal() なら標準で対応)必須
Tab / Shift+Tabフォーカスがダイアログ内だけを循環する(外に漏れない)必須
Enter / Spaceフォーカス中のボタンを実行必須

補足

上のキー操作は <dialog>.showModal() を使えばすべて自動です。 自前の div モーダルだと、これらを一つひとつ JavaScript で実装しなければなりません。

必要な WAI-ARIA / ロール

付ける場所属性 / ロール意味
ダイアログ本体<dialog> 要素暗黙の role="dialog" を持つ。showModal() でモーダルとして開く。
ダイアログ本体aria-labelledby="見出しのid" または aria-labelダイアログに名前を付ける(中の見出しを指すのが定番)。
ダイアログ本体aria-describedby(任意)補足の説明文を関連付ける。
背面のコンテンツ(自動)inert 相当showModal() なら背面が自動で操作不可・読み上げ対象外になる。手動なら inert を付ける。

実装:推奨パターン(Good)

良い例 / 推奨

ネイティブ <dialog>showModal() で開く。フォーカス管理・Esc・背面 inert が標準で付いてくる。

マークアップ:

<button type="button" id="open">設定を開く</button>

<dialog id="dialog">
  <h2>表示設定</h2>
  <p>テーマやフォントサイズをここで変更できます。</p>
  <button type="button" id="close">閉じる</button>
</dialog>

開閉のスクリプト(たったこれだけ):

const dialog = document.getElementById('dialog');
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');

// showModal() だけで、フォーカストラップ・Esc で閉じる・
// 背面を操作不可(inert)にする、が標準で手に入る。
openBtn.addEventListener('click', () => dialog.showModal());
closeBtn.addEventListener('click', () => dialog.close());

// 閉じたあとは、開いたトリガーへ自動でフォーカスが戻る。

補足

ダイアログに名前を付けるため、<dialog aria-labelledby="title"> のように 中の見出しの id を指しておくと、スクリーンリーダーが 「表示設定, ダイアログ」と読み上げてくれます。

アンチパターン(Bad)

下は <div> を重ねただけの自作モーダルです。マウスでは開閉できますが、開いたまま Tab を押すと フォーカスが背面に抜け、Esc でも閉じません。

div オーバーレイの壊れたモーダル

試してみよう:開いた状態で Tab を連打 → 背面の「設定を開く」やページ内リンクにフォーカスが移ってしまう。Esc を押しても閉じない。× はキーボードでは押せない。

<!-- ❌ div オーバーレイの自作モーダル -->
<button type="button" id="open">設定を開く</button>

<div id="overlay" class="overlay" style="display:none">
  <div class="modal">
    <h2>表示設定</h2>
    <p>テーマやフォントサイズをここで変更できます。</p>
    <!-- span なのでキーボードで閉じられない -->
    <span class="x" onclick="hide()">×</span>
  </div>
</div>

悪い例 / 避ける

この実装の問題点:

  • Tab が背面に漏れる — フォーカストラップが無く、見えない要素に移動できてしまう。
  • Esc で閉じない — キー処理を自分で書いていない。
  • 背面が inert でない — スクリーンリーダーで背面まで読めてしまう。
  • 閉じるのが <span> — フォーカスできず、キーボードでは閉じられない。
  • フォーカスの移動/復帰が無い — 開いてもダイアログに入らず、閉じても元へ戻らない。

ポイント

どうしても <div> で作るなら、フォーカストラップ・Esc・背面 inert・ フォーカス復帰・role="dialog" / aria-modal="true"すべて自前で実装する必要があります。<dialog> なら、その大半が無料です。

実装チェックリスト


原文(英語):Dialog (Modal) Pattern — W3C APG(新しいタブで開きます)