トピックを検索
質問が読み上げられない確認ダイアログ
削除の確認ダイアログに名前がなく、説明も関連付けられていないため、スクリーンリーダーはフォーカスのある「プロジェクトを削除」ボタンしか読み上げません。
<div class="confirm" role="dialog" aria-modal="true"> 問題
スクリーンリーダーの利用者は、何が消えるのかを聞かないまま「プロジェクトを削除」に置かれます。
プロジェクトの一覧の各行に「削除」ボタンがあります。「春のキャンペーン」の「削除」を押すと、ページの上にダイアログが開きます。「「春のキャンペーン」を削除しますか?」の下に、プロジェクトと24個のファイルが削除され、元に戻せないという一文があり、「キャンセル」と「プロジェクトを削除」が並びます。見た目に問題はありません。しかし、フォーカスは最初から「プロジェクトを削除」にあり、スクリーンリーダーは「ダイアログ、プロジェクトを削除、ボタン」と読んで、それで終わります。先へ進むつもりでEnterを押すと、プロジェクトは消えます。ダイアログを開いた「削除」ボタンも行と一緒に消えたため、フォーカスは戻る先を失い、ページのbody、つまりどこでもない場所に残ります。
ダイアログにはロールがあっても名前がありません。見出しは画面に表示されていますが、ダイアログに関連付けられていないため、スクリーンリーダーは「ダイアログ」としか伝えられません。ARIAではダイアログに名前が必須で、名前のないダイアログは4.1.2の問題として報告されます。何が起きるかを書いた説明も関連付けられていないため、フォーカスが入っても読まれません。
「プロジェクトを削除」の後、スクリプトは「削除」ボタンへフォーカスを戻そうとしますが、そのボタンは行と一緒に取り除かれています。フォーカスはbodyに落ちます。スクリーンリーダーは何も読み上げられず、次のTabの行き先はブラウザーによって変わります。このようにフォーカスを失うことは、2.4.3を満たしません。
残る問題は、フォーカスの開始位置です。WCAGはダイアログをどのボタンから始めるかを定めていませんが、ARIA Authoring Practicesは、取り消せない操作では最も影響の小さいボタンから始めるよう勧めています。確認ダイアログは誤操作を止めるためのものです。「プロジェクトを削除」から始めると、止めるはずのEnterがそのまま実行されます。
キーボードで操作する人は、最初からフォーカスが「プロジェクトを削除」にある状態でダイアログに出会います。マウスの利用者はボタンを選んで押すため、ほとんど気づきません。
10グループ中2グループに影響
自動チェックは role="dialog" と aria-modal="true" を確認し、何も報告しないことがあります。axeのダイアログ名のチェックはベストプラクティス扱いで、しかもページの読み込み時に開いているダイアログしか見ません。どのボタンが取り消せない操作なのか、押したボタンの行ごと消えた後にフォーカスがどこへ行くのかは、自動チェックにもマークアップを読むAIにも分かりません。画面上は何もおかしく見えないため、目視の確認も通ります。「確認ダイアログを追加して」と頼まれたAIは、まさにこの形を書きがちです。慎重に見えて、フォーカスは実行ボタンにあります。
試してみる
Tabで「削除」ボタンへ移動してEnterを押し、続けてもう一度Enterを押してください。
ダイアログが開いたときにどのボタンにフォーカスがあるかを見て、スクリーンリーダーの読み上げを確認してください。削除した後は、フォーカスの位置を確かめてください。
問題あり 修正できますか?
プロジェクト
- 春のキャンペーン · 24ファイル
- 年次報告書 · 9ファイル
- チームの写真 · 132ファイル
スクリーンリーダーの読み上げ:
(Tabで「削除」へ移動してEnter)
修正方法
見出しでダイアログに名前を付け、説明を関連付け、「キャンセル」から始め、削除後はページに残る場所へフォーカスを移します。
- 1
aria-labelledbyで見出しを指し、ダイアログに名前を付ける。 - 2説明を
aria-describedbyで関連付け、最初のフォーカスは「キャンセル」に置く。 - 3項目が消えた後は、ページに残る要素へフォーカスを移す。
<div id="page"> <h2>プロジェクト</h2> <ul> <li data-name="春のキャンペーン" data-files="24"> 春のキャンペーン・24ファイル <button type="button" class="delete">削除</button> </li> … </ul></div> <div class="confirm" role="dialog" aria-modal="true" hidden> <h2 class="confirm-title">「春のキャンペーン」を削除しますか?</h2> <p class="confirm-message">プロジェクトと24個のファイルを削除します。元に戻せません。</p> <button type="button" class="cancel">キャンセル</button> <button type="button" class="ok">プロジェクトを削除</button></div>HTML標準のdialog要素
<div id="page"> <h2 id="projects-heading" tabindex="-1">プロジェクト</h2> <ul> <li data-name="春のキャンペーン" data-files="24"> 春のキャンペーン・24ファイル <button type="button" class="delete">削除</button> </li> … </ul></div> <dialog id="confirm-delete" role="alertdialog" aria-labelledby="confirm-title" aria-describedby="confirm-message"> <form method="dialog"> <h2 id="confirm-title">「春のキャンペーン」を削除しますか?</h2> <p id="confirm-message">プロジェクトと24個のファイルを削除します。元に戻せません。</p> <button value="cancel" autofocus>キャンセル</button> <button value="delete">プロジェクトを削除</button> </form></dialog>この方法を選ぶ理由:まずHTMLの要素を使う
画面上の文言は修正後とまったく同じです。背後のページはinertになり、Escapeキーも効くため、目視と簡単なキーボードの確認なら通ります。しかし、見出しも説明もダイアログに関連付けられておらず、フォーカスは「プロジェクトを削除」から始まり、その後の trigger.focus() は、たった今取り除かれたボタンを指しています。
role="alertdialog" は応答が必要なダイアログであることをスクリーンリーダーに伝え、aria-labelledby は見出しを名前にし、aria-describedby は説明を一緒に読ませます。autofocus でフォーカスが「キャンセル」から始まるため、うっかり押したEnterではプロジェクトは消えません。showModal() は背後のページを操作できない状態にし、Escapeキーで閉じます。行が消えた後は、フォーカスがbodyに落ちず、一覧の見出しへ移ります。
どちらも基準を満たします。HTML標準の dialog を選んでください。背後のページの無効化、Escapeキー、キャンセル後にフォーカスを戻す処理をブラウザーが行うため、残るコードはこのガイドで扱う部分だけです。独自のダイアログは、「修正前」のようにフレームワークやデザインシステムが独自のオーバーレイを描画している場合に、最も小さな変更で済む方法です。ライブラリの確認ダイアログを使う場合は、両方のボタンに名前を付けられるか、最初のフォーカスをどちらに置くか選べるかを確認してください。
ダイアログの形は、操作の重さで決まります。
- 元に戻せる操作:ダイアログは使いません。すぐに削除し、項目のあった場所に「元に戻す」を置きます。読む前や元に戻す前に消えるトーストを参照してください。
- 元に戻せない操作:確認ダイアログを使い、「キャンセル」から始め、失われるものを、役に立つなら数も添えて書きます(「24個のファイル」)。
- 元に戻せず、他の人にも影響する操作(共有ワークスペースの削除など):項目の名前を入力するまで削除ボタンが使えないようにし、まだ押せない理由をダイアログ内に書きます。
この例のように、見出しと実行ボタンには操作と対象を書きます(「「春のキャンペーン」を削除しますか?」「プロジェクトを削除」)。スクリーンリーダーが読む名前は、指している見出しの内容しだいです。「よろしいですか?」と「OK」では、誰にも何が起きるか分かりません。
window.confirm() はモーダルで、スクリーンリーダーにも読まれますが、多くのブラウザーでは「OK」から始まり、ボタン名も「OK」と「キャンセル」に固定されるため、このようなダイアログにはできません。
画面で「キャンセル」を先に表示するなら、マークアップでも先に置き、Tabの順序を表示の順序に合わせます。実行ボタンを赤で示す場合も、色だけに頼らないでください。ラベルの「プロジェクトを削除」がすでに内容を伝えています。
削除後のフォーカスの行き先として、一覧の見出しは安全です。次の行の「削除」ボタンは続けて削除する人には速いものの、誤って押しやすいため、そこには置きません。項目が消えたことをページ上で伝えていない場合は、読み上げられるメッセージを追加してください。画面に出ても読み上げられないステータスメッセージで扱っています。
フォーカスがダイアログに入ること、背後のページ、Escapeキーなど、モーダル全体の動作は背後にフォーカスが残るモーダルを参照してください。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得
修正の確認
マウスを使わない5つの確認
確認方法: キーボード、スクリーンリーダー、ズーム、強制配色
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。共通のダイアログコンポーネントを変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。削除はページ上だけで行い、サーバーへの送信やそのエラー、失敗したときの処理は省略しています。alertdialogをどこまで自動で読み上げるかはスクリーンリーダーによって異なるため、実際に使用する支援技術で操作全体を確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。