トピックを検索
キーボードで最後にしか届かないCookieバナー
同意管理のスクリプトがバナーをフッターの後に追加し、画面下部に固定するため、ページの手前に表示されるのに、Tabとスクリーンリーダーがたどる順序では最後になります。
document.body.append(banner) 問題
キーボードとスクリーンリーダーの利用者は、ページ全体を通り過ぎた後でしかCookieバナーに出会えず、その途中でバナーにページの一部を隠されます。
Harbour Museumのトップページには、ヘッダーに四つのリンク(来館案内、展示、イベント、ショップ)、本文に六つのリンク(開館時間、ガイドツアーの予約、親子向けコース、船の模型展示室、カフェのメニュー、バリアフリー案内)、フッターに三つのリンク(お問い合わせ、報道関係の方へ、採用情報)があります。初めて訪れると、画面の下部をCookieバナーが覆います。「訪問数の集計とサイトの改善にCookieを使用します」という文と、「すべて許可」「すべて拒否」「Cookieを選ぶ」があります。マウスを使う晴眼の利用者には、最初に目に入るものです。ページの先頭からTabキーを押すと、フォーカスはメニュー、本文、フッターを順に進み、14回目でようやく「すべて許可」に届きます。その途中で、画面の下部までスクロールしたリンクはバナーの下に入り、フォーカスがあるのに見えません。先頭から読むスクリーンリーダーは、バナーより先にフッターを読みます。
同意管理のスクリプトは、ページの読み込み時にバナーを作り、document.body.append() でフッターの後に追加します。そのうえでCSSが画面下部に固定します。バナーが表示される位置と、ソース上の位置には何の関係もなく、Tabとスクリーンリーダーはソースの順序に従います。達成基準2.4.3は、ページを操作できる状態を保つ順序でフォーカスが移ることを求めています。バナーはすべての手前に表示され、回答するまで毎回画面の一部を覆うのに、順序ではすべての後にあります。キーボードの利用者は、ページの一部が隠れたままページ全体を進まないと、バナーを消せません。その途中で達成基準2.4.11も満たさなくなります。バナーの下にスクロールしたリンクは、フォーカスがある間、完全に隠れています。
スマートフォンではバナーが画面の半分を占めることがあり、その下に入る部分も、下を通るTabの回数も増えます。
10グループ中3グループに影響
ボタンには名前があり、バナーには文もあるため、ページを走査するだけのチェックは通過します。順序が問題になるのは、バナーが表示される位置と比べたときだけで、ソースの順序を調べるチェックはその二つを比べません。また、バナーは選択が保存されていない初回訪問でしか出ません。Cookieを保持したままのチェックや、誰かが許可した後に実行するチェックは、バナーを一度も見ません。多くのバナーは外部の同意管理サービスのもので、チームの誰もそのコードを読んでいません。生成されたバナーのコードも、要素を作ってbodyの末尾に追加し、画面下部に固定するという、よくある手順をたどりがちです。
修正方法
バナーをページの先頭に置くか、Escapeで閉じられるモーダルダイアログとして開き、誰もが最初に届くようにします。
- 1初回訪問では、Tabとスクリーンリーダーが最初に届くのがバナーである。
- 2表示中のバナーが、フォーカス中の要素を隠さない。
- 3バナーから離れるには、Tab、回答、Escapeのいずれかでよく、再読み込みは不要である。
<body> <header class="site-header">…</header> <main>…</main> <footer>…</footer> <div class="cookie-banner"> <p>訪問数の集計とサイトの改善にCookieを使用します。</p> <button type="button">すべて許可</button> <button type="button">すべて拒否</button> <a href="/cookies">Cookieを選ぶ</a> </div></body>ページの先頭に置いたバナー
<body> <section class="cookie-banner" aria-labelledby="cookie-title"> <div class="cookie-question"> <h2 id="cookie-title">Harbour MuseumのCookieについて</h2> <p>訪問数の集計とサイトの改善にCookieを使用します。</p> <button type="button" value="accept">すべて許可</button> <button type="button" value="reject">すべて拒否</button> <a href="/cookies">Cookieを選ぶ</a> </div> <p class="cookie-saved" tabindex="-1" hidden> Cookieの選択を保存しました。Cookieのページでいつでも変更できます。 </p> </section> <header class="site-header">…</header> <main>…</main> <footer>…</footer></body>バナーはbodyの最後の要素なので、Tabでは最後の移動先、スクリーンリーダーでは最後に読まれる内容になります。一方で position: fixed により、ページを読み込んだ瞬間から画面の下部に重なって表示されます。
バナーがbodyの最初にあるため、最初のTabの移動先になり、スクリーンリーダーも最初に読みます。見出しのある section なので、ランドマークの一覧にも名前付きで表示されます。ページに重ねず、先頭に通常の配置で置くため、どこでもフォーカスを隠しません。選択した後は、同じ場所の確認メッセージにフォーカスが移るため、利用者が分からない位置に放り出されることもありません。スクリプトでバナーを作る場合は、append() ではなく document.body.prepend(banner) で追加します。
ページの先頭に置いたバナーと、同意ダイアログは、どちらも基準を満たします。先頭のバナーは変更が小さく、利用者はページを読んで使いながら、準備ができたときに回答できます。正しい位置に置けば、それ以上の管理も不要です。代わりに、初回訪問ではページが下に押し下げられます。ダイアログは、ページを使う前に全員に回答を求め、フォーカス、背後の操作不能、Escapeをブラウザーが処理します。代わりに、スクリーンリーダーの利用者を含む全員が、ページの内容を知る前に足止めされます。ページが本当に回答を待つ必要がある場合を除き、先頭のバナーを選んでください。高さ分の余白だけを確保する方法は、移動できないバナーのための応急処置です。フォーカスは見えるようになりますが、バナーは最後のままです。
多くのCookieバナーは同意管理サービスが追加するもので、マークアップを直接編集できません。次の順に試し、うまくいった時点で止めてください。
- サービスの設定を確認する。多くのサービスでは、レイアウト(上部のバー、下部のバー、モーダル)と、ページ内のどこにバナーを入れるかを選べます。bodyの先頭に入る上部のバーか、Escapeで閉じられるモーダルを選びます。
- 設定がない場合は、バナーが表示された時点で自分で移動する。
MutationObserverでbodyを監視し、バナーの要素が追加されたらdocument.body.prepend()で移動します。移動したら監視を止め、バナーのボタンが動くことを確認してください。バナーを作り直して元の位置に戻すスクリプトもあります。 - バナーを移動できない場合は、最後の修正のように
scroll-padding-bottomで高さ分の余白を確保し、少なくともフォーカスが見えるようにする。これは応急処置であり、対応の終わりではありません。 - 再現手順を添えてサービス提供元に問題を報告し、アクセシビリティ適合報告書(ACR)を求める。直るまでは、アクセシビリティ方針の既知の問題に記載します。
あわせて、フォーカスを閉じ込める実装がないか確認してください。外部サービスのコードによく見られ、2.1.2(キーボードトラップなし)を満たしません。
- ページを操作不能にしないのに、選択するまでスクリプトがバナー内でTabを循環させる。
- 同意ダイアログがEscapeを無効にし、閉じる方法が「許可」しかない。
- バナーの上に開く「Cookieを選ぶ」パネルが、閉じたときにフォーカスを戻さず、文書の先頭に落とす。
詳しくは、keyboard-traps と modal-focus-management のガイドを参照してください。外部サービスのスクリプトは、自社のリリースと関係なく変わります。サービスの更新や設定の変更のたびに、「修正の確認」の手順を繰り返してください。
ページを読み込んだときにバナーの最初のボタンへ focus() を呼べば、キーボードの利用者は全員そこから始められるため、近道に見えるかもしれません。しかし、ページを操作不能にしないバナーでは、これは避けてください。それだけで達成基準を満たさなくなるわけではありませんが、ソースの先頭に置けば、すでに最初のTab移動先になり、最初に読まれます。フォーカスを移すと、得るものより失うものが大きくなります。
- スクリーンリーダーの利用者が現在位置を見失う。ページを読み込むと、スクリーンリーダーはタイトルを読み上げ、先頭から読み始めます。フォーカスがボタンへ飛ぶと、タイトルが途中で切れることが多く、どのページにいるのか分からないまま「すべて許可、ボタン」と聞くことになります。
- 画面拡大を使う利用者の表示が、バナーの表示位置(多くは画面の下部)へ引っ張られ、見ようとしていたページの上部から離れる。
- 先に読みたいキーボードの利用者が、いつ回答するかを選べず、何よりも先にバナーに対応させられる。
例外は同意ダイアログです。回答するまでページを使えない場合は、フォーカスをダイアログ内に移す必要があり、showModal() がそれを行います。初期設定でバナーにフォーカスを移す同意管理サービスもあります。無効にする設定を探し、なければサービス提供元への報告に含めてください。
GOV.UK Design Systemと同じく、バナーはスキップリンクより前に置きます。最初のTabでバナーに、2回目のTabでスキップリンクに届き、回答した後はスキップリンクが再び最初になります。同じガイダンスは、フォーカスした内容を覆うおそれがあるため、バナーを画面に固定すること自体を勧めていません。デザイン上、画面下部への固定を続ける場合は、ソースでは先頭に置いたまま、focus-not-obscured のガイドのように高さ分の余白を確保します。
できればバナーはサーバーが返すHTMLに含めます。そうすれば、スクリプトが動く前から正しい位置にあり、読み始めた後でページが押し下げられることもありません。
AI コーディングアシスタントで修正しますか? このガイドを Markdown で取得
修正の確認
マウスを使わない5つの確認
確認方法: キーボード、スクリーンリーダー、ズーム
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。同意管理サービスやその設定を変更した際は、再度確認します。
制限
このサンプルは、どのアプリケーションでも同じ結果になることを保証するものではありません。扱うのはバナーへの届き方と、バナーが隠すものであり、有効な同意の条件は扱いません。各選択肢で何を保存するか、Escapeで回答しないまま閉じてよいかは、同意を管理する担当者と決めてください。最後に置かれたバナーだけで2.4.3を満たさないかどうかは、診断する人によって判断が分かれます。ただし、その下に隠れるフォーカスは、起きるたびに2.4.11を満たしません。バナーやダイアログが取り除かれた後にTabがどこから続くかはブラウザーによって異なるため、対象のブラウザーで確認してください。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
コードを更新しても、使いやすさを保つために
ガイドの内容を、コーディングエージェントやCIで使える開発ルールにまとめます。更新後は、キーボードとスクリーンリーダーで再テストし、問題が再発していないか確認します。