WCAG 達成基準 · Level AA

WCAG 2.4.11: フォーカスの非遮蔽(最小)

WCAG 2.4.11 では、UI コンポーネントがキーボードフォーカスを受け取ったとき、固定ヘッダー、クッキーバナー、チャットウィジェットなどの作成者が作成したコンテンツによって完全に隠されないことが求められています。この基準は、キーボードユーザーが常にページ上の自分の位置を確認できるようにするものであり、ナビゲーションとユーザビリティにとって不可欠です。

このルールの意味

WCAG 2.4.11「フォーカスの非非表示(最小)」は、WCAG 2.2 で導入されたレベルAAの達成基準であり、よくあるが深刻なアクセシビリティの問題、つまりフォーカスされた要素がページ上の別のコンテンツレイヤーの背後に完全に隠れてしまう状況に対処するものです。この達成基準は、任意のユーザーインターフェイスコンポーネントがキーボードフォーカスを受け取ったとき、そのコンポーネントが制作者が作成したコンテンツによって完全に隠されてはならないと定めています。

ここでのキーワードは完全にです。WCAG 2.4.11 は最低限の基準を定めており、フォーカスされた要素の一部が可視のままであることだけを要求します。たとえば、固定ナビゲーションバーがフォーカスされたボタンの上半分に重なっていても、下半分がまだ見えているのであれば、技術的には 2.4.11 を満たしていることになります。より厳格な対応する達成基準であるレベルAAAの WCAG 2.4.12「フォーカスの非非表示(強化)」はさらに踏み込み、フォーカスされたコンポーネントが制作者が作成したコンテンツによって一切隠されないこと、つまり部分的にであっても隠されないことを要求します。

この問題の原因となる制作者が作成したコンテンツとして最も一般的なのは、スティッキーまたは固定位置のヘッダーやフッター、クッキー同意バナー、チャットサポートウィジェット、通知トースト、モーダルオーバーレイ、フローティングアクションボタン、モバイル対応レイアウトのボトムナビゲーションバーなどです。これらの要素は、position: fixedposition: sticky、高い z-index 値といった CSS プロパティを使ってレンダリングされ、通常の文書フローの上に浮かぶように配置されるため、フォーカスを受け取るインタラクティブ要素を覆い隠してしまう可能性があります。

適合(pass)とは、キーボードユーザーがインタラクティブ要素を Tab キーで移動していく際、スティッキーなUI要素が一部に重なっていたとしても、フォーカスされた要素の視覚的なインジケーターの一部が常に画面上に見えている状態を意味します。不適合(fail)は、フォーカスされた要素が制作者が作成したレイヤーの下に完全に隠れてしまい、ユーザーが現在フォーカスがどこにあるのかを示す視覚的手がかりをまったく得られない場合に発生します。ブラウザ自身のツールバーなど、ブラウザがレンダリングするコンテンツは制作者が作成したコンテンツとは見なされず、この達成基準の対象外です。同様に、ユーザー自身が位置を変更したコンテンツ(ユーザーがドラッグ可能なパネルなど)も対象外です。

この達成基準は、デフォルトでキーボードフォーカス可能な、あるいは tabindex によってフォーカス可能にされたすべてのインタラクティブな HTML 要素に適用されます。これには、<a><button><input><select><textarea>、および tabindex='0' や正の tabindex 値を持つ要素が含まれます。

なぜ重要か

キーボードナビゲーションは、複数のユーザーグループにとってアクセシビリティの根幹です。マウスを使えない運動障害のある人は、キーボード、スイッチデバイス、シップ・アンド・パフコントローラーなどに全面的に依存してウェブページを移動します。視覚障害のある人は、スクリーンリーダーとキーボードを組み合わせて利用します。スクリーンリーダーを使わずにキーボードナビゲーションを行うロービジョンの人は、自分がどこにいるかを理解するために、視覚的なフォーカスインジケーターに大きく依存しています。フォーカスされた要素がスティッキーヘッダーやフローティングウィジェットの背後に隠れてしまうと、これらのユーザーは事実上迷子になります。Tab キーを何度も押して位置を推測するか、タスク自体を諦めざるを得ません。

具体的な現実のシナリオを考えてみましょう。手の可動域が限られた車椅子ユーザーが、トルコの航空会社のウェブサイトで航空券予約フォームに入力しているとします。彼らは Tab キーを使ってフォームフィールドを移動します。ページのビューポート下部にはスティッキーなクッキー同意バナーがあります。ユーザーが最後の確認用チェックボックスに Tab で移動すると、そのチェックボックスはクッキーバナーの下に完全にレンダリングされます。ユーザーには、そのチェックボックスにフォーカスが当たっているという視覚的な手がかりが一切ありません。Tab キーの押下が有効だったのか、もう一度 Tab を押すべきなのか、そのチェックボックスが必須なのかどうかも判断できません。この混乱は、フォームの放棄、送信漏れ、あるいは意図しない操作を引き起こす繰り返しのキー入力につながりかねません。

この影響は、障害のあるユーザーにとどまりません。スピードのためにキーボードナビゲーションを好むパワーユーザー(開発者、データ入力担当者、熟練ユーザー)も、明確なフォーカスの可視性から恩恵を受けます。世界保健機関によると、世界で約 13 億人が何らかの障害を抱えており、そのうちのかなりの割合が支援技術やキーボードナビゲーションに依存しています。トルコに限ると、トルコ統計機構(TÜİK)は、人口の約 12.7% が何らかの障害を持つと報告しており、これはデジタルプラットフォーム上でのフォーカス管理の改善から直接恩恵を受ける可能性のある人が約 1,000 万人いることを意味します。

ビジネスの観点から見ると、隠れたフォーカスインジケーターはタスク放棄率を高め、コンバージョンを低下させ、組織を法的・規制上のリスクにさらします。この達成基準を満たしていないサイトは、トルコの法律に基づいて要求されるアクセシビリティ監査にも不合格となる可能性が高く、公式な「Erişilebilirlik Logosu(アクセシビリティロゴ)」の取得や維持が危うくなります。

関連する axe-core のルール

WCAG 2.4.11 は、axe-core において手動テストが必要な項目として分類されています。この違反を直接検出する自動化された axe-core のルールは存在しません。なぜ自動ツールがこの問題を確実に検出できないのかを理解することは、チームがより良い手動テストプロセスを構築するうえで役立ちます。

  • 手動テストが必要 — 固定配置によるフォーカスの非表示: 自動ツールは、フォーカスされた要素が視覚的に隠れているかどうかを検出できません。なぜなら、その判断にはページをレンダリングし、実行時にすべての固定またはスティッキー要素の境界矩形を計算し、フォーカス時点での現在のフォーカス要素の境界矩形と比較する必要があるからです。ビューポートの寸法、スクロール位置、ページの動的な状態(クッキーバナーがすでに閉じられているかどうかなど)が結果に影響します。静的解析や DOM の検査だけでは、あらゆる可能な状態においてこの視覚的な計算を確実に再現することはできません。axe-core は、この達成基準を手動とフラグ付けしています。なぜなら、人間のテスター、あるいは高度なビジュアルリグレッションツールが、実際にページを Tab で移動し、フォーカスされたコンポーネントが重なり合うレイヤーの背後に消えてしまわないかを観察する必要があるからです。
  • 手動テストが必要 — 動的なオーバーレイコンテンツ: 多くの遮蔽要素は、初回ページロード後に JavaScript を通じて動的に挿入されます。サードパーティのチャットウィジェット、GDPR コンプライアンスバナー、プロモーションのポップイン、フローティングナビゲーションメニューなどです。初期の DOM スナップショットを解析する自動スキャナーは、これらの要素をまったく取得しないか、ユーザーの実際の体験を反映しない折りたたみ状態や非表示状態で取得してしまう可能性があります。完全にレンダリングされ動的に変化する状態のページをキーボードで操作するユーザーによる手動テストだけが、これらのシナリオを確実に検出する方法です。

テスト方法

  1. まず自動アクセシビリティスキャンを実行する。 axe DevTools(ブラウザ拡張機能)、Chrome DevTools の Lighthouse、あるいは CI に統合した axe-core スキャンを使用して、ページ上の自動検出可能な問題を特定します。2.4.11 自体は手動検証が必要ですが、自動スキャンによって、フォーカスインジケーターの欠如(2.4.7)や、重なり合う要素を示唆する誤った z-index スタッキングなど、関連する問題が表面化する場合があります。手動テストを始める前に、潜在的に問題があるとフラグ付けされた要素をすべてメモしておきます。
  2. ページ上のすべての固定およびスティッキー要素を特定する。 ブラウザの DevTools を開き、ページの CSS を検査します。position: fixedposition: sticky を使用している要素を検索します。また、高い z-index 値(例: 100 以上)を持つ要素も確認します。ヘッダー、フッター、バナー、チャットウィジェット、クッキー通知など、これらの要素をすべて記録します。これらがフォーカスされたコンポーネントを隠す最有力候補だからです。ブラウザウィンドウのサイズを変更して、タブレットやモバイル幅など、さまざまなビューポートサイズをシミュレートします。スティッキー/固定要素は、ブレークポイントによって挙動が変わることが多いためです。
  3. キーボードによるフルタブトラバーサルを行う。 フォーカスがページ上部から開始されるよう、インタラクティブ要素以外の場所をクリックしてから、Tab キーを繰り返し押して、フォーカス可能なすべての要素を順に移動します。各要素がフォーカスを受け取る際を注意深く観察します。固定ヘッダーやフッターが重なりやすいビューポートの上端や下端付近に現れる要素には特に注意してください。もしどこかの時点で、可視のフォーカスリングやハイライトされた要素が固定レイヤーの背後に完全に消えてしまった場合、それは 2.4.11 の不適合です。Shift+Tab を使って逆方向にもトラバーサルを行います。スクロール位置やレイアウトが異なる場合があるためです。
  4. NVDA と Firefox でテストする。 NVDA(Windows 向けの無料・オープンソースのスクリーンリーダー)を起動し、Firefox を開きます。NVDA のブラウズモードを有効にし、Tab キーでページを移動します。NVDA は各フォーカス要素を音声で読み上げますが、同時に画面も視覚的に確認します。NVDA がフォーカスを読み上げているにもかかわらず、重なり合うコンテンツのせいで視覚的なフォーカスインジケーターが見えない要素があればメモします。この組み合わせはトルコおよび世界的に広く利用されており、重要な支援技術のペアです。
  5. macOS/iOS の VoiceOver と Safari でテストする。 VoiceOver(Mac では Command+F5)を有効にし、Tab キーまたは VoiceOver のナビゲーションジェスチャーを使ってフォーカス可能な要素を移動します。iOS ではスワイプジェスチャーを使用します。特にモバイルでは、ナビゲーションバーやクッキーバナーがビューポートの大きな割合を占めることがあるため、フォーカスされた要素が固定 UI レイヤーの下に隠れておらず、視覚的に表示されていることを確認します。
  6. JAWS と Chrome でテストする。 JAWS(Job Access With Speech)を起動し、Chrome を使用します。すべてのインタラクティブ要素を Tab で移動し、JAWS のカーソルが固定レイヤーの下に視覚的に隠れている要素に移動してしまう瞬間がないかを確認します。JAWS は企業や政府機関の環境で広く利用されており、この組み合わせは公共部門や企業サイトのテストに特に重要です。
  7. レイアウトを変化させるユーザー操作後にテストする。 クッキーバナーを閉じる、ナビゲーションメニューを開閉する、通知トーストを表示する、チャットウィジェットを開くなどの操作を行います。各状態変化の後に、再度 Tab トラバーサルを実行し、新しいレイアウトでフォーカスが隠れる事例が新たに発生していないかを確認します。動的コンテンツは、ユーザー操作後にのみ現れる 2.4.11 の不適合の一般的な原因です。
  8. 複数のスクロール位置でテストする。 長いページの中部や下部までスクロールし、その領域の要素を Tab で移動します。固定ヘッダーは、ページ上部付近の要素は隠さないかもしれませんが、ユーザーがスクロールダウンした際にビューポート上端に現れる要素を覆い隠すことがあります。どのスクロール位置とフォーカス要素の組み合わせでも、完全な視覚的隠蔽が起こらないことを確認します。

修正方法

スティッキーヘッダーがフォーカスされたリンクに重なる — 不適切な例

<!-- Sticky header with no scroll offset accommodation -->
<header style='position: fixed; top: 0; left: 0; width: 100%; height: 60px; z-index: 1000; background: white;'>
  <nav>
    <a href='/'>Home</a>
    <a href='/about'>About</a>
  </nav>
</header>

<main>
  <!-- No scroll-margin-top set; focused link at top of main scrolls under the header -->
  <a href='/section-one'>Go to Section One</a>
  <a href='/section-two'>Go to Section Two</a>
</main>

スティッキーヘッダーがフォーカスされたリンクに重なる — 適切な例

<!-- Fixed header with scroll-margin-top applied to all focusable elements -->
<header class='site-header'>
  <nav>
    <a href='/'>Home</a>
    <a href='/about'>About</a>
  </nav>
</header>

<main>
  <!-- scroll-margin-top ensures the browser scrolls the element into view
       with enough clearance so the fixed header does not cover it -->
  <a href='/section-one' class='content-link'>Go to Section One</a>
  <a href='/section-two' class='content-link'>Go to Section Two</a>
</main>

<!-- In your stylesheet: -->
<!-- .site-header { position: fixed; top: 0; height: 60px; z-index: 1000; } -->
<!-- * { scroll-margin-top: 70px; } -->
<!-- The 70px (header height + 10px buffer) keeps focused elements
     visible below the fixed header when the browser auto-scrolls. -->

クッキーバナーが下部のフォーカスされたフォームフィールドを覆う — 不適切な例

<!-- Cookie banner fixed at bottom with no accommodation for form fields above it -->
<div id='cookie-banner' class='cookie-overlay'>
  <p>We use cookies. <button>Accept</button></p>
</div>

<form>
  <label for='email'>Email Address</label>
  <input type='email' id='email' name='email'>

  <label for='confirm'>Confirm Subscription</label>
  <!-- This checkbox renders in the viewport area covered by the cookie banner -->
  <input type='checkbox' id='confirm' name='confirm'>

  <button type='submit'>Submit</button>
</form>

クッキーバナーが下部のフォーカスされたフォームフィールドを覆う — 適切な例

<!-- Cookie banner pushes page content up instead of overlapping it -->
<div id='cookie-banner' class='cookie-banner-flow'>
  <p>We use cookies. <button id='accept-cookies'>Accept</button></p>
</div>

<!-- In your stylesheet: -->
<!-- .cookie-banner-flow { position: static; } instead of position: fixed -->
<!-- Alternatively, if fixed positioning is required for design reasons,
     add scroll-margin-bottom to all focusable elements equal to the
     banner height, or apply padding-bottom to <body> equal to
     the banner height so content is never hidden beneath it. -->

<form>
  <label for='email'>Email Address</label>
  <input type='email' id='email' name='email'>

  <label for='confirm'>Confirm Subscription</label>
  <input type='checkbox' id='confirm' name='confirm'>

  <button type='submit'>Submit</button>
</form>

サードパーティチャットウィジェットがフォーカスされたボタンに重なる — 不適切な例

<!-- Third-party chat widget injected in bottom-right corner
     with a high z-index, obscuring the focused Submit button -->
<div id='chat-widget' class='chat-fixed'>
  <!-- Injected by third-party script, covers 120x120px in bottom-right -->
</div>

<section class='cta-section'>
  <!-- Button positioned in the bottom-right area of the layout;
       when focused, it is completely hidden beneath the chat widget -->
  <button type='button' class='cta-button'>Get a Free Quote</button>
</section>

サードパーティチャットウィジェットがフォーカスされたボタンに重なる — 適切な例

<!-- Approach 1: Reposition the chat widget to avoid overlapping focusable content -->
<div id='chat-widget' class='chat-fixed-adjusted'>
  <!-- Widget repositioned to bottom-left, away from the CTA button -->
</div>

<!-- Approach 2: Ensure the button has scroll-margin that keeps it
     clear of the widget when focused -->
<section class='cta-section'>
  <button type='button' class='cta-button'>Get a Free Quote</button>
</section>

<!-- Approach 3: Use JavaScript to detect focus events on elements
     near the widget's bounding box and temporarily collapse or
     move the widget so the focused element is visible. -->
<script>
  // Example: collapse widget when nearby element gains focus
  document.querySelectorAll('.cta-button').forEach(function(btn) {
    btn.addEventListener('focus', function() {
      var widget = document.getElementById('chat-widget');
      if (widget) widget.setAttribute('aria-hidden', 'false');
      // Additional logic to reposition or minimize widget
    });
  });
</script>

よくある間違い

  • ヘッダーやフッターに position: fixed を適用しながら、フォーカス可能な要素に scroll-margin-topscroll-margin-bottom を追加していない。 ブラウザのネイティブなフォーカススクロールは、固定された重なり要素を考慮しないため、フォーカスされた項目はビューポートの上端または下端までスクロールされ、固定レイヤーの背後に隠れてしまいます。* { scroll-margin-top: [header-height]px; } のようなグローバルな CSS ルールを使うと、これを効率的に解決できます。
  • 固定ヘッダーのオフセットとして body { padding-top: 60px; } を設定しながら、キーボードフォーカス用の同等の scroll-margin-top を追加し忘れている。 このパディングは、初期表示時にコンテンツが隠れないようにしますが、キーボードフォーカスによってブラウザの自動スクロールが発生した際、スクロール対象がヘッダーの背後に滑り込んでしまう可能性があります。両方のアプローチを併用する必要があります。
  • プロモーションバナーやチャットウィジェットに z-index: 9999 を設定し、そのフットプリント内にどのフォーカス可能要素が含まれるかを確認していない。 高い z-index は、オーバーレイがフォーカスされたボタンやリンクを含むすべてのものの上にレンダリングされることを保証してしまい、2.4.11 の不適合の最も一般的な根本原因となります。
  • JavaScript でクッキーバナーを閉じる際に、レイアウトから即座に削除していない。 バナーの DOM 要素が visibility: hiddenopacity: 0 のまま残り、スペースを占有したり、ゼロ以外の z-index を持ち続けていると、ブラウザのレンダリングによっては、視覚的にフォーカスされた要素を隠し続ける可能性があります。
  • サードパーティウィジェット(ライブチャット、アクセシビリティオーバーレイ、GDPR マネージャーなど)を埋め込みながら、キーボードフォーカスの可視性への影響を検証していない。 サードパーティスクリプトは、非常に高い z-index を持つ固定位置要素を挿入することがよくあります。チームは、アクセシビリティ QA プロセスの一環として、これらの統合を明示的にテストしなければなりません。
  • レスポンシブブレークポイント変更後に 2.4.11 をテストしていない。 デスクトップでは高さ 60px のスティッキーナビゲーションバーが、タブレットではハンバーガーメニューが開いたときに 120px に拡大し、突然より広い領域を覆って、デスクトップでは問題なかったブレークポイントで新たな不適合を生むことがあります。
  • <h2><section> などのアンカーターゲットにだけ scroll-margin-top を適用し、<input><button><a> といったインタラクティブ要素には適用していない。 アンカーのスクロールオフセットはよく対処されますが、アンカー以外の要素へのキーボードフォーカスによる自動スクロールも同様に影響を受けるため、同じ CSS ルールでカバーする必要があります。
  • フォーカスされた要素がスクリーンリーダーに読み上げられているからといって、視覚的にも見えていると決めつけている。 スクリーンリーダーはアクセシビリティツリーにアクセスしており、視覚的なレンダリングにはアクセスしていません。要素は固定オーバーレイの背後に完全に隠れていても、NVDA や JAWS によって正しく読み上げられることがあります。これは別個の問題であり、2.4.11 は視覚的なフォーカスの可視性、つまり視覚的にページを見るキーボードユーザーのための要件です。
  • シングルページアプリケーション(SPA)のルート変更後にフォーカスの隠蔽をテストしていない。 React、Vue、Angular などのアプリでは、ルート遷移時に更新された状態で固定ヘッダーが再レンダリングされることが多く、ナビゲーション中のフォーカス管理によって、ユーザーが新しいページを見る前に、再マウントされたスティッキーヘッダーの背後に即座に隠れてしまう要素にフォーカスが置かれることがあります。
  • 自動テストツールだけに頼り、ページに対して自動ルールからのフラグがないからといって 2.4.11 の問題は存在しないと結論づけている。 axe-core にはこの達成基準用の自動ルールが存在しないため、自動レポートがクリーンであっても、ページが適合しているとは限りません。すべてのビューポートサイズとページ状態にわたる手動のキーボードトラバーサルが必須です。

トルコのアクセシビリティ規制との関係

WCAG 2.4.11「フォーカスの非非表示(最小)」は、トルコで形成されつつある法的アクセシビリティ枠組みに直接関係します。2025年6月21日に官報第 32933 号で公布されたトルコ大統領通達 2025/10 は、WCAG 2.2 と整合する必須のデジタルアクセシビリティ要件を定めています。この通達は、トルコのデジタルインクルージョンへの取り組みを大きく拡大するものであり、トルコのデジタル市場で事業を行う幅広い主体に対して、法的に拘束力のある義務を課すものです。

この通達は、公的機関や政府機関、eコマースプラットフォーム、銀行や金融サービス提供者、病院や医療機関、20万件以上の加入者を持つ通信会社、旅行代理店、民間の交通会社、国民教育省(MoNE)に認可された私立学校など、幅広い組織を対象としています。これらすべての主体は、自らのデジタルインターフェイス(ウェブサイト、モバイルアプリケーション、ウェブベースのツール)が WCAG 2.2 のレベルAAに準拠するようにすることが求められています。

WCAG 2.4.11 は WCAG 2.2 におけるレベルAAの達成基準であるため、このルールへの準拠は対象となる主体にとって任意ではありません。家族・社会サービス省(Aile ve Sosyal Hizmetler Bakanlığı)が発行する公式の「Erişilebilirlik Logosu(アクセシビリティロゴ)」の取得や維持を目指す組織は、レベルAAの適合を満たさなければなりません。アクセシビリティロゴは、デジタルインクルージョンへの取り組みを示す公的なシグナルとして機能し、トルコの消費者や政府調達機関からますます期待されるようになっています。

実務的には、スティッキーナビゲーションヘッダーを持つトルコの公的機関のウェブサイト、常時表示のプロモーションバナーを持つ eコマースサイト、フローティングヘルプウィジェットを備えた銀行ポータル、クッキー同意オーバーレイを持つ医療予約システムなどは、すべて 2.4.11 に基づくフォーカスの隠蔽についてテストと是正を行う必要があります。この達成基準の不適合は、特に複雑でマーケティング要素の多いインターフェイスで発生しやすく、まさに通達の対象となる大規模商業事業者が運営するサイトの典型例です。

通達の対象となる組織は、定期的なアクセシビリティ監査サイクルに 2.4.11 のテストを組み込むべきです。この達成基準は手動テストを必要とするため、自動スキャンだけに依存することでは、適切な注意義務を果たしたとは見なされません。完全な準拠を目指すトルコの組織は、適合文書化の一環として、すべてのビューポートサイズと動的なページ状態にわたる手動のキーボードトラバーサルテストを実施し、アクセシビリティロゴの申請や更新の前に、特定されたフォーカス隠蔽の問題を是正ロードマップの一部として解決することが推奨されます。