トピックを検索
フォーカス順の最後に回る、フローティングボタンとパネル
浮いた「予約する」バーと、それが開くシートがフッターの後に書かれているため、キーボードとスクリーンリーダーの利用者は、ページの主な操作にも、その日付にも、ページ全体の後でしかたどり着けません。
問題
キーボードとスクリーンリーダーの利用者は、ページの「予約する」ボタンにページ全体の後でしかたどり着けず、それで日付を開いても、またページ全体の後でしか日付に届きません。
ツアー会社の予約ページが、スマートフォン向けにデザインされています。「旧市街ウォーキングツアー」「2時間、1人$25から」、写真、集合場所と「地図で開く」リンク、タップで開く3つの質問(「段差のないルートですか?」「雨の場合は?」「キャンセルできますか?」)、「128件のレビューをすべて読む」リンクが並び、フッターに「ヘルプセンター」「利用規約」「お問い合わせ」があります。ページを開いた時点から、画面の下に白いバーが浮いていて、「$25から」と「予約する」ボタンがあり、いつでも親指で予約できます。「予約する」を押すとバーのすぐ上にシートが開き、日付を選んで「次へ」へ進めます。
キーボード利用者がページを開き、見えている「予約する」へ進もうとTabを押します。フォーカスは代わりに「地図で開く」へ移り、続いて各質問、レビューのリンク、フッターの3つのリンクへ進みます。「予約する」は9回目のTabです。スクリーンリーダー利用者が最初から読むと、フッターまでページ全体を聞いた後で、ようやく「予約する、ボタン、折りたたみ」にたどり着きます。
開発者はこの問題に気づき、バーをマークアップの上へ、価格の後に移します。「予約する」は最初のTabになりました。ところが、もう一つの問題が残ります。Enterキーを押すとシートが開きますが、最初の日付へ進もうとTabを押すと、フォーカスは「地図で開く」へ戻ります。日付に届くのは、またページ全体の後、9回先です。
バーもシートも、ページの最後、フッターの後に書かれています。親要素に切り取られないよう、予約ウィジェットやコンポーネントライブラリーが浮いて表示するものを置くことの多い場所です。CSSは画面の下に描きますが、Tabとスクリーンリーダーは見た目には従いません。マークアップの順にページを進み、マークアップの上ではバーとシートは最後です。マークアップの順序を画面上の順序に合わせる達成方法C27は、この食い違いを解消するものです。
達成基準2.4.3 フォーカス順序は、意味と操作を損なわない順序でフォーカスが移ることを求めています。バーはページの主な操作として最初から内容の手前に描かれ、スマートフォンではこれが唯一の「予約する」ボタンです。それなのにすべての後に来るため、キーボードとスクリーンリーダーの利用者は、ほかの人が1回のタップで届くものに、ページ全体を通ってたどり着くことになります。フォーカス順序は見た目の配置に従う必要はなく、2.4.3の解説も本文の後に書いたナビゲーションを適合する例に挙げているため、浮いた操作要素が最後に来てよいかどうかは、ページに対する判断です。ページの最後にある「ページ上部へ」ボタンやチャットの起動ボタンは事情が違います。ページの目的ではなく、利用者もそこにあると考えるためです。
シートも、失敗例F85のとおり同じ達成基準を満たしません。ボタンで開くダイアログやメニューに、フォーカスが移らず、次のTabでも届かない場合です。ボタンを移すと最初の問題は解消しますが、シートは最後に残るため、こちらの問題は残ります。F85のテストには後半もあり、そちらはこのシートも満たしています。Escapeキーで閉じると、フォーカスは「予約する」に戻ります。シートへ向かう途中、フォーカスはシートの後ろも通るため、見えなくなることもあります。
ページが長いほど、バーもシートも遠くなります。写真のギャラリー、地図、十数件のレビュー、充実したフッターのある実際の予約ページなら、「予約する」まで数十回、日付までさらに同じくらいのTabが必要になることがあります。
10グループ中3グループに影響
- 全盲 (影響あり)
- 弱視 (影響あり)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 届く範囲・力 (影響なし)
- 認知 (影響なし)
- 光過敏 (影響なし)
- 手の操作が限られる キーボードやスイッチの利用者には、ページを開いた時点から「予約する」が見えていますが、届くのはフッターの後、9回目のTabです。バーを上へ移しても、日付を開くと、次のTabはページの上へ戻り、日付はまた9回先になります。写真やレビューのある実際のページなら数十回です。
- 視覚を使わない スクリーンリーダー利用者は、フッターまでページ全体を聞いた後で「予約する」にたどり着き、予約ボタンがずっと画面にあったことを知らされません。日付を開いても、読まれるのはページの最後です。
- 視覚が限られる バーを拡大して見ている画面拡大の利用者には「予約する」が見えていますが、9回目のTabまでフォーカスはページの別の場所にあり、バーを上へ移しても、シートを開くと、拡大した表示がフォーカスを追ってシートから離れてしまいます。
一つひとつの部品に問題はありません。「予約する」は名前とaria-expandedを持つbuttonで、シートには見出し、凡例、ラベル付きの選択肢があり、どの操作要素にも到達できます。axeが報告することはありません。問題は順序にあり、ページがどの順序を意図しているかは人にしか判断できません。EaseWebの診断は、ウィンドウに固定された操作要素に、ページのフッターの後でしかTabが届かない場合を、確認が必要な項目として挙げるようになりました。「ページ上部へ」ボタンは最後にあってよく、チャットの起動ボタンとCookieバナーにはそれぞれ別の確認があるため、これらは除きます。2つ目の問題は検出結果として報告します。aria-expandedとaria-controlsを持つボタンを、1ページにつき12個まで一つずつ開き、フォーカスがパネルの中へ移らず、次のTabでも届かないとき、そのパネルを報告します。開くことを伝えないボタンのパネルや、外部サービスのフレームの中にあるパネルは、引き続き手作業でしか見つけられません。ページの上に浮くバーやシートをAIアシスタントに頼むと、overflowやz-indexの影響を避けるためにポータルやdocument.body.append()を使うことがあり、それが順序に何をもたらすかは説明されません。
試してみる
キーボードでツアーを予約してください。Tabを何回押しますか?
- ページの最初から、「予約する」に届くまでTabを押します。
- Enterで日付を開き、Tabを押します。フォーカスは最初の日付へ移りますか、ページの中へ戻りますか?
- 日付からShift + Tabを押し、もう一度シートを開いてEscapeで閉じます。
問題あり 修正できますか?
旧市街ウォーキングツアー
2時間、1人$25から
集合場所:マーケット広場の噴水。 地図で開く
段差のないルートですか?
はい。旧市街の平らな道だけを通ります。
雨の場合は?
開催します。市場のホールで屋根のある休憩を取ります。
キャンセルできますか?
はい。開始の24時間前まで無料です。
日付を選ぶ
フォーカス: –
修正方法
浮いたボタンを操作の対象となる内容と一緒に書き、パネルを、マークアップかpopovertargetでその直後に置きます。
- 1ページの主な操作である浮いたボタンは、フッターの後ではなく、マークアップ上でその内容と一緒に来る。
- 2パネルを開くボタンの次のTabでパネルの中へ移る。または、パネルが開くときにフォーカスを受け取る。閉じると、フォーカスはボタンに戻る。
- 3TabとShift + Tabの両方向の順序を、対応するすべてのブラウザーで確認する。
フッターの後にあるバーとシート
/* バーのための余白。ページの最後やフォーカスを覆わないようにする。 */html { scroll-padding-bottom: 5rem;}body { padding-bottom: 5rem;}.book-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; align-items: center; justify-content: space-between; /* 高さ4rem、下端から1rem:確保した5remに収まる。 */ height: 4rem; padding: 0 1rem; border-radius: 1rem; background: #fff;}.sheet { position: fixed; inset-inline: 1rem; bottom: 5.5rem; padding: 1rem; border-radius: 1rem; background: #fff;}内容と一緒のバーと、その直後のシート
/* バーのための余白。ページの最後やフォーカスを覆わないようにする。 */html { scroll-padding-bottom: 5rem;}body { padding-bottom: 5rem;}.book-bar { position: fixed; inset-inline: 1rem; bottom: 1rem; display: flex; align-items: center; justify-content: space-between; /* 高さ4rem、下端から1rem:確保した5remに収まる。 */ height: 4rem; padding: 0 1rem; border-radius: 1rem; background: #fff;}.sheet { position: fixed; inset-inline: 1rem; bottom: 5.5rem; padding: 1rem; border-radius: 1rem; background: #fff;}ボタン、その状態、閉じる処理、バーのための余白は、どれも正しく書かれています。間違っているのはバーの場所だけです。フッターの後にあるため、「予約する」は「地図で開く」、3つの質問、レビューのリンク、フッターの3つのリンクの後、9回目のTabです。シートはその直後にあるため、「予約する」に届けば、日付は次のTabです。
バーをマークアップ上で、それが繰り返す価格の直後に置き、シートをバーの直後に置きます。ほかは何も変わらず、position: fixedにより、画面上ではどちらも同じ場所に描かれます。「予約する」はページの最初のTabです。シートを開いた状態で「予約する」からTabを押すと最初の日付へ、「次へ」の後のTabは「地図で開く」へ移ってシートを閉じるため、フォーカスのあるリンクがシートの下に隠れることはありません。日付からのShift + Tabは「予約する」へ戻ります。閉じている間は、hiddenがシートを順序から外します。スクリーンリーダーで最初から読んでも、価格の後にバーが、その次にシートが読まれます。バーには達成方法C27、シートには、操作要素で表示する内容をその直後に置く達成方法SCR26を使っています。この方法は、position: fixedがウィンドウを基準にしていることが前提です。transform、filter、containを持つ親要素があると、バーとシートはその親を基準に固定され、親要素のz-indexによって後の内容がその上に描かれることもあります。親要素を確認してください。
3つとも「予約する」を価格と一緒に置き、2.4.3を満たします。シートをバーの直後に移す方法は、ほかに何も足さない最小の変更で、スクリーンリーダーで「予約する」から読み進めても次に日付に届くのは、どのブラウザーでもこの方法だけです。ただし、バーとシートの親要素がposition: fixedや重なり順に影響しないことが条件です。ポップオーバーは、開く、Escapeキーで閉じる、フォーカスを戻すためのスクリプトが要らず、「予約する」が展開されていることをスクリーンリーダーに伝え、マークアップがどこにあってもシートをすべての上に描きます。予約ウィジェット、テーマ、コンポーネントライブラリーがパネルをページの最後に置く場合は、この方法を使います。ブラウズモードでは、シートは引き続きそこで読まれます。シートへフォーカスを移す方法は、外部サービスのスクリプトが追加するシートのように、移動もポップオーバーへの変更もできないシートのためのものです。シートからTabやShift + Tabで出るとページの最後へ移るため、これだけでは不十分です。
浮いたボタンは、その内容がある位置に書きます。「予約する」バーは、ツアー名と、それが繰り返す価格の後に、「絞り込み」ボタンは絞り込む商品の前に、「カートに入れる」バーは商品名と価格の後に置きます。画面のどの角に描くかは、キーボードやスクリーンリーダーには何の意味もありません。ページの上のほうにページ自体の「予約する」ボタンがある場合、浮いたバーは同じ操作への2つ目の手段です。ページ自体のボタンに早く届くなら、バーは最後のままでもかまいません。
この例のシートは、後ろのツアーの説明を読めるよう、モーダルではありません。シートがページを覆い、後ろの内容を使えない場合は、モーダルダイアログにします。フォーカスがその中へ移り、閉じるまで中にとどまります。背後にフォーカスが残るモーダルを参照してください。
ここでのどの方法も、フォーカスがシートの中から「予約する」以外のページ上の要素へ移ると、シートを閉じます。Tabで閉じてフォーカスを見失う検索パネルが、フォーカスが部品全体から出たときに閉じるのと同じです。開いたままにすると、Tabで進んだ先をシートが覆い、フォーカスのある操作要素を完全に隠すことがあります。達成基準2.4.11 フォーカスの非隠蔽(最低限)が、利用者が開いた内容にそれを認めるのは、フォーカスを動かさずに隠れた部分を表示できる場合だけです。リスナーはフォーカスの移り先であるrelatedTargetを読むため、フォーカスがほかの操作要素に移らないシートの余白のクリックや、ページの外へのTabでは閉じません。バー自体は、scroll-padding-bottomとページの終わりの余白で5rem分を空けているため、フォーカスもページの最後も覆いません。拡大するとページを覆う、浮いた購入バーを参照してください。
予約ウィジェットやコンポーネントライブラリーは、親要素に切り取られないよう、バー、ポップオーバー、メニュー、シートを、ReactのcreatePortalやVueのTeleportのようなポータルを使ってbodyの最後に描くことがよくあります。その場で描くオプションや、HTML標準のポップオーバーを使うオプションを探してください。どちらもない場合はパネルへフォーカスを移し、Escapeキーでフォーカスがボタンに戻ることを確認します。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得、または全ガイドをコーディングエージェントで使う。
修正の確認
マウスを使わない6つの確認
確認方法: キーボード、スクリーンリーダー
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。バー、シート、ページのレイアウトを変更した際や、予約ウィジェット、コンポーネントライブラリー、外部サービスのスクリプトを更新した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。バー、シート、ツアーはこの例のものです。チャット、ヘルプ、フィードバックのパネルも含め、ページ上のすべての浮いたボタンと、それが開くすべてのパネルを、表示される最も長いページで確認してください。浮いたボタンが順序の最後にあってよいかどうかは、ページに対する判断です。「ページ上部へ」ボタンは最後でよく、ページの主な操作はそうではありません。チャットのパネルはメッセージの読み上げも必要です(メッセージを読み上げないチャットウィジェット)。ポップオーバーのフォーカス順序はブラウザーによって異なるため、対応するブラウザーそれぞれで確認してください。コード内のクラス名は、実際のものに置き換えてください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。