WCAG 達成基準 · Level A
WCAG 2.1.2: キーボードトラップの禁止
WCAG 2.1.2 は、キーボードユーザーがコンポーネント内に閉じ込められることが決してないようにすることを求めています。つまり、キーボードを使ってフォーカスをUI要素の中に移動できる場合は、キーボードだけを使ってそのフォーカスを外へ移動することも可能でなければなりません。この基準は、運動障害のある人やスクリーンリーダー利用者を含む、キーボードナビゲーションのみに依存しているユーザーにとって不可欠です。
- Level A
- Wcag
- Wcag 2 2 a
- 操作可能
- アクセシビリティ
この規則が意味すること
WCAG 2.1.2「キーボードトラップなし」は、原則「操作可能」におけるレベルAの達成基準です。そこでは次のように定められています。「キーボードインターフェースを用いてページのコンポーネントにキーボードフォーカスを移動できる場合、そのコンポーネントからもキーボードインターフェースのみを用いてフォーカスを移動できなければならない。また、フォーカスを移動するために修飾されていない矢印キーやTabキー以外の操作が必要な場合は、フォーカスを移動する方法について利用者に知らせなければならない。」
実務的には、キーボード利用者がTabキーでフォーカスを移動できるウェブページ上のあらゆるインタラクティブコンポーネントは、同じくTab操作でそこから抜け出せなければならない、という意味です。キーボードトラップとは、利用者がウィジェット、ダイアログ、カスタムフォームコントロール、埋め込みメディアプレーヤー、その他のフォーカス可能な領域に移動したあと、標準的なキーボード操作ではそこから出られなくなり、事実上「閉じ込められて」しまう状況を指します。
この達成基準は、キーボードフォーカスを受け取ることができるすべてのHTML要素を対象とします。たとえば、<input>、<select>、<textarea>、<button>、<a>タグといったネイティブなインタラクティブ要素に加え、tabindex、ARIAロール、JavaScriptによるフォーカス管理によってフォーカスを受け取るカスタムウィジェットも含まれます。また、チャットウィジェット、ビデオプレーヤー、地図の埋め込み、日付ピッカー、リッチテキストエディタなどのサードパーティ製埋め込みコンポーネントにも同様に適用されます。
コンポーネントがこの達成基準に適合するのは、キーボード利用者が常に標準的なキー(通常はTab、Shift+Tab、Escape、矢印キー)を使ってそのコンポーネントからフォーカスを移動できる場合、またはフォーカスを移動するために非標準のキーボード操作が必要な場合に、その方法がページ上で明確に案内されている場合です。コンポーネントが不適合となるのは、フォーカスがその中に固定されてしまい、キーボードでアクセス可能な退出経路が存在しない場合、あるいは唯一の退出手段がマウスクリックやタッチジェスチャーなど、キーボード以外の入力に依存している場合です。
WCAGは、例外を狭く認めています。意図的でアクセシブルなデザインパターンの一部として、一時的にキーボードフォーカスをコンポーネント内に制限することは許容されます。代表的な例がモーダルダイアログです。モーダルダイアログは、開いている間はフォーカスをダイアログ内に閉じ込めるべきです(背後の隠れたコンテンツと誤ってやり取りするのを防ぐため)。しかし同時に、ダイアログを閉じてフォーカスを解放するキーボード操作手段を必ず提供しなければなりません。たとえば、Escapeキーのハンドラや、Tabで到達できる明確にラベル付けされた閉じるボタンなどです。このような、意図的で脱出可能なフォーカスの封じ込めはキーボードトラップではなく、モーダルダイアログパターンの正しい実装です。利用者がまったく抜け出せないときに初めて、トラップは不適合となります。
なぜ重要なのか
キーボードトラップは、ウェブサイトが抱えうるアクセシビリティ上の不具合の中でも最も深刻なもののひとつです。他の多くのアクセシビリティ問題が特定の利用者にとって体験を低下させるのに対し、キーボードトラップは利用者がページの利用を継続すること自体を完全に妨げる可能性があります。これは、廊下に物理的な障壁を置き、迂回路を一切用意しないことに等しいと言えます。
最も深刻な影響を受けるのは、マウスを操作できず、完全にキーボードナビゲーションに依存している運動障害や身体障害のある利用者です。これには、反復性ストレス障害、多発性硬化症、パーキンソン病、四肢麻痺、脳性まひなどの状態の人々が含まれます。世界保健機関によると、約13億人、すなわち世界人口の16%が何らかの重大な障害とともに生活しており、その中でも運動機能障害は大きな割合を占めています。こうした利用者にとって、チェックアウトページ、ログインフォーム、カスタマーサービスのチャットウィジェットなどでキーボードトラップに遭遇することは、そのタスクを完全に完了できないことを意味します。
スクリーンリーダー利用者(主に全盲およびロービジョンの利用者)もまた大きな影響を受けます。NVDA、JAWS、VoiceOverといったスクリーンリーダーは、完全にキーボードコマンドによってナビゲーションを行います。フォーカスがコンポーネント内に閉じ込められると、スクリーンリーダー利用者はTabキーを押すたびに同じ要素が繰り返し読み上げられ、ページを前後に進む手段がなくなります。これは非常に混乱を招き、利用者はブラウザタブを閉じるかページを再読み込みするしかなくなり、それまでの作業内容を失ってしまいます。
次のような現実のシナリオを考えてみてください。手の可動性が限られている利用者が、商品を購入するためにトルコのECサイトを訪れます。商品をカートに追加し、チェックアウトに進みます。チェックアウトページには、カスタムJavaScriptフレームワークで構築されたサードパーティ製の住所オートコンプリートウィジェットが含まれています。このウィジェットは、住所フィールドにフォーカスが当たると候補のドロップダウンリストを開きます。しかし開発者は、このドロップダウンを閉じるためのキーボードイベント処理を追加し忘れていました。そのため、利用者が住所フィールドにTabで入ってドロップダウンが表示されると、Tabで抜け出すことも、Escapeを押すことも、マウスクリックなしにドロップダウンを閉じることもできません。利用者は購入手続きを完了することが完全に妨げられます。これは些細な不便ではなく、そのサービスからの完全な排除です。
障害の有無にかかわらず、キーボードトラップは、速度のためにキーボードナビゲーションを好むパワーユーザー、マウスの使用が制限されているエンタープライズ環境の利用者、ハードウェアキーボードを備えた一部のモバイルやハイブリッドデバイスの利用者にも影響します。したがって、キーボードトラップを解消することは、障害のある利用者だけでなく、より広い利用者層の体験向上にもつながります。
関連するaxe-coreルール
WCAG 2.1.2は手動テストを必要とします。キーボードトラップを直接かつ確実に検出できる自動のaxe-coreルールは存在しません。これは、自動ツールがDOMの静的構造を解析して動作するためです。自動ツールはフォーカス可能な要素を特定し、Tab順序を確認し、ARIA属性を検証することはできますが、人間のテスターが行うような、完全なインタラクティブなキーボードナビゲーション体験をシミュレートすることはできません。キーボードトラップは通常、実行時にキーボードイベントを横取りまたは抑制するJavaScriptのイベント処理ロジックから生じます。この挙動はDOM構造からは見えず、静的解析では推測できません。
- 手動テストが必須 — 専用のaxe-coreルールは存在しない: axe-coreには、キーボードトラップを自動的に検出するルールは同梱されていません。なぜなら、この不具合は本質的に「挙動」に関わるものだからです。トラップは、利用者が実際にTabキーでコンポーネントに入り、そこから抜け出そうとしたときにのみ顕在化します。自動スキャナがこれを再現するには、ページ上のすべてのフォーカス可能要素に対して順次キーボードナビゲーションをシミュレートし、関連するすべてのJavaScriptイベントリスナーを発火させ、そのうえでフォーカスが意図した領域から抜け出せたかどうかを判断する必要があります。これは複雑なページに対しては計算量的に実現不可能な問題です。さらに、何がトラップであり、何が(モーダルのような)意図的なフォーカス封じ込めなのかを判断するには文脈的な判断が必要であり、自動ルールが信頼性高く行うことはできません。テスターは、axe DevToolsやLighthouseを用いてフォーカス可能要素やTab順序の問題を特定することを出発点とし、そのうえでページ上のすべてのインタラクティブ領域をキーボードだけで手動ナビゲーションし、トラップが存在しないことを確認する必要があります。
テスト方法
- ベースラインとして自動アクセシビリティスキャンを実行する。 axe DevTools(ブラウザ拡張)を開くか、Chrome DevToolsでLighthouseのアクセシビリティ監査を実行します。どちらのツールもキーボードトラップを直接は検出しませんが、フォーカス可能要素、欠落したARIAロール、リスクの高いインタラクティブコンポーネントを示唆する誤ったTab順序などを特定できます。カスタムウィジェット、埋め込みiframe、サードパーティコンポーネント、モーダルダイアログ、ドロップダウンメニュー、日付ピッカー、カルーセル、リッチテキストエディタなどに注目してください。これらはキーボードトラップの最も一般的な発生源です。
- マウスを切り離すか、使わないようにする。 キーボードによる手動テストを有効に行うには、マウスを使用してはなりません。テスト中にうっかりマウスに頼ってしまわないよう、マウスを手の届かない場所に置くか、OSの設定で無効にします。
- TabキーとShift+Tabキーだけを使ってページ全体をナビゲートする。 ブラウザのアドレスバーまたはページ上部から開始し、Tabキーを繰り返し押してフォーカスがどこに移動するかを観察します。各ステップでフォーカスインジケータを目視で追跡します。各インタラクティブコンポーネント、特にカスタムウィジェット、埋め込みコンテンツ、複雑なUI要素に到達したら、TabまたはShift+Tabを押したときにフォーカスがそのコンポーネントからスムーズに抜け出し、ページ上の次または前のフォーカス可能要素に移動することを確認します。
- 各インタラクティブコンポーネントを個別にテストする。 モーダルダイアログの場合: モーダルを開き、フォーカスがその中に移動することを確認したうえで、Escapeキーを押し、フォーカスがトリガー要素に戻ることを確認します。ドロップダウンメニューの場合: ドロップダウンを開き、その中をナビゲートしてからEscapeキーを押し、ドロップダウンが閉じてフォーカスがトリガーに戻ることを確認します。日付ピッカー、カラーピッカーなどのウィジェットの場合: Tabで中に入り、操作したあと、Tabで抜け出してフォーカスが外に出ることを確認します。埋め込みiframe(地図、ビデオプレーヤー、チャットウィジェットなど)の場合: iframeにTabで入り、その中をナビゲートし、Tabでメインページに戻れることを確認します。
- NVDAとFirefoxでテストする。 NVDAを起動し、Firefoxでページを開き、Tabキーでインタラクティブ要素を移動します。Tabを押すたびにNVDAが新しい要素を読み上げるか、それとも同じ要素に戻っているように聞こえるかに注意してください。Tabでコンポーネントを通り過ぎてフォーカスを進められない場合、それはキーボードトラップです。
- macOSのVoiceOverとSafariでテストする。 VoiceOver(Command + F5)を有効にし、Safariでページを開き、Tabキーでナビゲートします。Tabを押すたびにVoiceOverが新しい要素をアナウンスし、抜け出せない領域にフォーカスが固定されることがないことを確認します。
- JAWSとChromeでテストする。 JAWSを起動し、Chromeでページを開き、Tabキーで全インタラクティブコンポーネントをナビゲートします。特に、JavaScriptによるフォーカス管理を行っているコンポーネントを重点的にテストします。JAWSはアクセシビリティツリーとやり取りするため、視覚的なテストだけでは見えないトラップを明らかにすることがあります。
- 非標準の退出メカニズムがないか確認する。 あるコンポーネントから抜け出すのにTab、Shift+Tab、Escape以外のキーが必要な場合、そのことがページ上で明確に利用者に伝えられているか確認します。たとえば、画面上の説明文、ツールチップ、またはフォーカスがコンポーネントに入ったときのARIAライブリージョンによるアナウンスなどです。
修正方法
Escape処理のないモーダルダイアログ — 不適切な例
<!-- モーダルは開くがEscapeキーのハンドラがなく、
キーボードで到達可能な閉じるボタンもない -->
<div id='modal' role='dialog' aria-modal='true' aria-labelledby='modal-title'>
<h2 id='modal-title'>Confirm Your Order</h2>
<p>Are you sure you want to place this order?</p>
<button onclick='submitOrder()'>Confirm</button>
<!-- 閉じるボタンもEscapeキーリスナーもないため、
キーボード利用者は閉じ込められる -->
</div>
Escape処理のあるモーダルダイアログ — 適切な例
<!-- 適切なフォーカストラップ、Escapeハンドラ、
アクセシブルな閉じるボタンを備えたモーダル -->
<div id='modal' role='dialog' aria-modal='true' aria-labelledby='modal-title'>
<h2 id='modal-title'>Confirm Your Order</h2>
<p>Are you sure you want to place this order?</p>
<button onclick='submitOrder()'>Confirm</button>
<!-- 閉じるボタンはTabで到達可能で、
キーボード利用者がモーダルから退出できる -->
<button id='modal-close' onclick='closeModal()' aria-label='Close dialog'>Cancel</button>
</div>
<script>
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeModal(); // Escapeキーでモーダルを閉じ、フォーカスをトリガーに戻す
}
});
function closeModal() {
document.getElementById('modal').hidden = true;
document.getElementById('modal-trigger').focus(); // フォーカスを開いた要素に戻す
}
</script>
Tabキーイベントをすべて捕捉するカスタムドロップダウン — 不適切な例
<!-- カスタムドロップダウンがTabを含むすべてのkeydownイベントを横取りし、
フォーカスが外に出るのを妨げている -->
<div id='custom-select' tabindex='0' role='combobox' aria-expanded='true'>
<ul role='listbox'>
<li role='option' tabindex='-1'>Option 1</li>
<li role='option' tabindex='-1'>Option 2</li>
<li role='option' tabindex='-1'>Option 3</li>
</ul>
</div>
<script>
// バグ: すべてのキーボードイベントを捕捉し、Tabに対してもpreventDefaultを呼び出すため、
// 利用者はこのコンポーネントからTabで抜け出せなくなる
document.getElementById('custom-select').addEventListener('keydown', function(e) {
e.preventDefault(); // これがキーボードトラップを生む
if (e.key === 'ArrowDown') { /* move focus down */ }
if (e.key === 'ArrowUp') { /* move focus up */ }
});
</script>
Tabキーイベントをすべて捕捉するカスタムドロップダウン — 適切な例
<!-- 適切な例: 内部ナビゲーションに使う矢印キーに対してのみpreventDefaultを行い、
TabとEscapeは通常どおり動作させて利用者が退出できるようにする -->
<div id='custom-select' tabindex='0' role='combobox' aria-expanded='false'>
<ul role='listbox' hidden>
<li role='option' tabindex='-1'>Option 1</li>
<li role='option' tabindex='-1'>Option 2</li>
<li role='option' tabindex='-1'>Option 3</li>
</ul>
</div>
<script>
document.getElementById('custom-select').addEventListener('keydown', function(e) {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault(); // 内部ナビゲーションに使うキーに対してのみ既定動作を抑止
// オプション間でフォーカスを移動
}
if (e.key === 'Escape' || e.key === 'Tab') {
// preventDefaultを呼び出さない — TabとEscapeをそのまま伝播させ、
// ブラウザが自然にこのコンポーネントからフォーカスを移動できるようにする
closeDropdown();
}
});
</script>
退出経路のないサードパーティiframeの埋め込み — 不適切な例
<!-- すべてのTabキー入力を吸収し、
親ページにフォーカスを戻さない埋め込みチャットウィジェットiframe -->
<iframe
src='https://example-chat-widget.com/embed'
title='Customer support chat'
width='350'
height='500'>
</iframe>
<!-- iframe内部のJavaScriptがTabイベントを消費し、
利用者をiframe内に閉じ込めてしまう -->
退出経路のないサードパーティiframeの埋め込み — 適切な例
<!-- iframeを埋め込んでキーボード利用者をトラップするのではなく、
ボタンでチャットを新しいウィンドウに開く -->
<button
id='open-chat'
onclick='window.open("https://example-chat-widget.com", "_blank", "noopener")'>
Open Customer Support Chat (opens in new window)
</button>
<!-- iframeをどうしても使う必要がある場合は、その前に目に見えるスキップリンクを追加し、
キーボード利用者が望めばバイパスできるようにする -->
<a href='#after-chat-widget' class='skip-link'>Skip chat widget</a>
<iframe
src='https://example-chat-widget.com/embed'
title='Customer support chat'
width='350'
height='500'>
</iframe>
<span id='after-chat-widget' tabindex='-1'></span>
よくある間違い
keydownリスナー内で、特定のキーに限定せずにevent.preventDefault()を呼び出すこと — フォーカス可能なコンポーネント上のすべてのキーボードイベントに対してpreventDefault()を適用すると、TabとShift+Tabが機能しなくなり、即座にキーボードトラップが発生します。preventDefault()は、コンポーネント内部で処理する必要がある特定のキー(リストボックスの矢印キーなど)にのみ限定して適用してください。- フォーカスをモーダル内に移動させるだけで、Escapeキーのハンドラや閉じるボタンを用意しないモーダルダイアログを構築すること — 開発者は、モーダルパターンの「フォーカスを中に入れる」部分だけを正しく実装しがちですが、モーダル内でのフォーカス封じ込めが許容されるのは、常にキーボードでアクセス可能な退出手段がある場合に限られることを忘れがちです。すべてのモーダルはEscapeキーを処理し、Tabで到達可能な閉じるボタンを含めなければなりません。
- コンテナ要素に
tabindex='-1'を設定してTab順序から外しつつ、JavaScriptでプログラム的にその要素へフォーカスを移動できるようにすること —element.focus()によってtabindex='-1'の要素にフォーカスを移動した場合、その要素から抜け出すための自然なTabの移動先が存在しません。追加のキーボード処理が実装されていないと、利用者がそこに取り残される可能性があります。 - サードパーティウィジェット(チャット、地図、ビデオ)を、キーボードトラップの有無を監査せずに埋め込むこと — ベンダーが提供する埋め込みウィジェットは、必ずしもキーボードアクセシブルに作られているとは限りません。サイト運営者は、サードパーティの埋め込みを含め、自身のページ上のすべてのコンテンツに責任を負います。埋め込みコンポーネントは必ず手動でテストし、キーボード利用者をトラップする場合は、スキップリンクで囲むか、キーボードに安全な代替手段に置き換えてください。
- モーダルやドロワーのためにフォーカストラップを実装したものの、コンポーネントを閉じたときにトラップを解除しないこと — フォーカスを制限するJavaScriptが、モーダルを閉じたあとも適切にクリーンアップされないと、見えなくなったレイヤー上でTabイベントを取り続け、利用者をトラップし続けることがあります。
- デザイン上の理由からダイアログの閉じるボタンをCSSの
visibility: hiddenやdisplay: noneで隠し、代替のキーボード退出手段を提供しないこと — 閉じるボタンが視覚的には隠されていてもアクセシビリティツリーからは除外されていない場合、スクリーンリーダー利用者はそれを見つけることができます。しかし、アクセシビリティツリーからも隠されている場合、退出手段が存在しない可能性があります。視覚的に控えめなスタイルであっても、すべての閉じるメカニズムがキーボード操作可能であることを確認してください。 - 候補リストを開き、Tabキー入力をすべて候補の循環に回してしまうカスタムオートコンプリートやタイプアヘッドコンポーネントを構築すること — 利用者はEscapeキーで候補リストを閉じ、その後Tabで次のフォームフィールドに移動できなければなりません。Tabを内部ナビゲーションに転用するオートコンプリートウィジェットは、この達成基準に違反します。
- TinyMCE、CKEditor、Quillなどのリッチテキストエディタのテストを忘れること — これらのコンポーネントは内部で独自のキーボードインタラクションを管理しており、キーボードトラップの頻出源です。Escapeキーまたは明示されたキー操作でエディタを退出し、ページの通常のTab順序にフォーカスが戻ることを必ず確認してください。
- ネイティブHTML要素を使っているコンポーネントはキーボードトラップになりえないと想定すること — フォーム内の
<select>要素であっても、そのblurイベントをJavaScriptで上書きすればフォーカスをトラップしうるのです。セマンティックなHTMLを使っているからといって、カスタムJavaScriptのイベント処理が上乗せされている場合にキーボードトラップが起こらないと保証されるわけではありません。 - コンポーネントから退出するのに非標準のキーが必要な場合に、画面上の説明を提供しないこと — WCAG 2.1.2は、非標準のキーで退出するコンポーネントを明示的に認めていますが、それは利用者にそのことが知らされている場合に限られます。ウィジェットから抜け出すのにF6や独自のキーコンビネーションが必要な場合は、そのことを利用者に明確に伝えなければなりません。理想的には、コンポーネントの近くに可視の説明を表示するか、フォーカスが入ったときにARIAライブリージョンでアナウンスします。
トルコのアクセシビリティ規制との関係
トルコの大統領通達2025/10は、2025年6月21日付官報第32933号で公布され、トルコで事業を行う幅広い公的・民間主体に対して拘束力のあるデジタルアクセシビリティ要件を定めています。この通達は、国際的に認められたウェブアクセシビリティ標準への準拠を義務付けており、そのベースラインとしてWCAG 2.1レベルAAへの整合を求めています。これには、レベルAのすべての達成基準、すなわちWCAG 2.1.2「キーボードトラップなし」も含まれます。
WCAG 2.1.2はレベルAの達成基準であり、これは最低限の準拠レベルを表します。この通達の下では、レベルAへの準拠はすべての対象主体にとって必須です。公的機関は、通達の公布から1年以内にこの準拠を達成することが求められ、民間部門の主体には2年の猶予が与えられています。
通達の対象となる主体は幅広く、あらゆるレベルの公的機関・政府機関、ECプラットフォームおよびオンラインマーケットプレイス運営者、銀行・金融サービス機関、病院・医療提供者、20万件以上の加入者を有する通信事業者、旅行代理店、民間の交通事業者、国民教育省(MoNE)に認可された私立学校などが含まれます。これは、これらの主体が運営するウェブサイトやウェブアプリケーションにおいてキーボードトラップへの対処を怠ることが、トルコの義務的アクセシビリティ規制への直接的な違反となることを意味します。
複雑なウェブアプリケーション、特にオンラインバンキングポータル、病院の予約システム、ECのチェックアウトフロー、通信事業者のアカウント管理ページなどでは、キーボードトラップの不具合が頻発しています。これらはまさに、カスタムウィジェット、モーダルダイアログ、サードパーティ埋め込みなど、キーボード利用者を意図せずトラップしがちな、インタラクションの多いJavaScript駆動のインターフェースです。そのため、トルコのコンプライアンス文脈において、WCAG 2.1.2は特に注意を払うべき達成基準です。
通達の対象となる組織は、キーボードトラップのテストをアクセシビリティ監査プロセスの「譲れない」一部として扱うべきです。自動ツールではキーボードトラップを確実に検出できないため、対象主体は、障害当事者を含む資格あるアクセシビリティ専門家による手動のキーボードテストに投資し、コンプライアンスへの道筋の一部とする必要があります。監査で特定されたキーボードトラップを是正しないことは、通達の下での法的リスクであるだけでなく、キーボードナビゲーションに依存してデジタルサービスを利用する運動障害および視覚障害のある利用者にとって重大なアクセス障壁となります。
