Skip to main content

2.1.2 No Keyboard Trap: what it requires and how to test it

If keyboard focus can get into something, the keyboard has to be able to get it out again.

Level A · Added in WCAG 2.0 · Last reviewed October 18, 2026

What 2.1.2 requires

If focus can be moved to a component using the keyboard, it must be possible to move focus away from it using only the keyboard. If leaving needs more than the usual keys (Tab, Shift+Tab, arrow keys or another standard exit such as Esc), the user must be told how. This is a plain-language summary. The normative text, including every exception, is in the W3C Recommendation, with the W3C's explanation in Understanding 2.1.2 (opens in a new tab) [1][2].

Exceptions and scope:

  • Intentional containment is allowed. W3C's Understanding document notes that restricting focus to a modal dialog or popover doesn't fail, as long as the user knows how to leave it (for example with Esc or a Close button).
  • Non-interference: this is one of the criteria that applies to all content on the page, even content that isn't relied on to meet other criteria, because a trap anywhere can block the whole page.

Why it matters

For someone who can't use a mouse, a keyboard trap is the end of the visit. Focus enters an embedded map, a chat widget, a video player or a date picker, and Tab just cycles inside it. There's no way to reach the rest of the page, so the only escape is to reload, and on a checkout that means starting over.

Common failures

Third-party embed that swallows Tab

A widget in an iframe handles every keystroke and never returns focus.

<!-- Fails -->
<iframe src="https://widget.example/chat"></iframe>

<!-- Better -->
<!-- Choose widgets that let Tab leave; test each vendor before shipping -->
<iframe src="https://widget.example/chat" title="Support chat"></iframe>

Custom component that cancels every Tab

A script prevents the default Tab behaviour and never lets focus leave the component.

/* Fails */
editor.addEventListener('keydown', e => {
  if (e.key === 'Tab') e.preventDefault();
});

/* Better */
editor.addEventListener('keydown', e => {
  if (e.key === 'Tab' && !e.shiftKey && insideIndentMode) e.preventDefault();
  // and document how to exit, e.g. 'Press Esc, then Tab'
});

Focus is held in the dialog, but Esc does nothing and the close button isn't focusable.

<!-- Fails -->
<div class="modal"><span class="x" onclick="close()">×</span>…</div>

<!-- Better -->
<dialog aria-labelledby="t"><h2 id="t">…</h2>…<button type="button">Close</button></dialog>
<!-- showModal() gives Esc-to-close by default -->

Focus loop after a dialog closes

The dialog is hidden but focus stays inside it, cycling through invisible controls.

/* Fails */
modal.hidden = true; // focus left inside

/* Better */
modal.close();
openerButton.focus();

Can automated testing check 2.1.2?

No. axe-core maps no rules to 2.1.2. A trap only exists when someone presses keys and focus fails to leave. That's behaviour, not markup.

axe-core 4.13.0 ruleWhat it checks
(none mapped)axe-core 4.13.0 maps no rules to this criterion

What automation can't decide: Whether focus can actually leave each component, whether a non-standard exit is explained, and whether a trap appears only in a certain state (a widget that has loaded, a modal opened from a specific button).

A clean automated result is never proof that 2.1.2 is met. See why sites pass axe-core but fail real screen reader users.

How to test it by hand

  1. Unplug or ignore the mouse. Start at the top of the page and press Tab until you have been through every interactive element and back to the browser chrome.
  2. Every time focus enters a component (embeds, editors, media players, maps, date pickers, chat widgets), try to leave with Tab, Shift+Tab and Esc.
  3. Open each dialog, menu and popover. Check you can close it with the keyboard and that focus returns to the control that opened it.
  4. If a component needs a special key to exit, check the instructions are visible or announced before focus enters it.
  5. Repeat on the key task pages (sign-in, checkout), where third-party widgets most often appear.

New to screen readers? Start with how to test a website with a screen reader and how to test keyboard-only navigation.

How Accessible helps (and where it stops)

This is where behavioural testing earns its keep. Real-user monitoring runs an observer for focus traps a keyboard user can't leave, along with dead-end Tab presses, in real sessions, and ranks each one by how many sessions met it (real-user monitoring). That includes traps in third-party widgets that only load for some visitors. Journeys operate the task keyboard-only, so a trap on the path through the task is reported at the step where it stopped. The assistant tools' keyboard walkthrough reports whether focus gets stuck in a loop on one page.

Where it stops: Accessible finds traps on the paths journeys take and in the sessions monitoring samples. A component nobody opened in those runs can still trap focus, so a manual keyboard pass over the whole page is still needed. No automated tool, ours included, can certify that a criterion is met.

Sources

  1. Understanding Success Criterion 2.1.2: No Keyboard Trap (opens in a new tab). W3C Web Accessibility Initiative. Accessed October 18, 2026.
  2. Web Content Accessibility Guidelines (WCAG) 2.2, Success Criterion 2.1.2 (opens in a new tab). W3C Recommendation. Accessed October 18, 2026.
  3. axe-core rule descriptions (opens in a new tab). Deque Systems, axe-core 4.13.0. Accessed October 18, 2026.
  4. WAI-ARIA Authoring Practices Guide: Dialog (Modal) pattern (opens in a new tab). W3C. Accessed October 18, 2026.

Meeting a success criterion is not the same as legal compliance, which depends on the rules that apply to you. No automated tool can certify conformance.

Frequently asked questions

Is a focus trap in a modal dialog a 2.1.2 failure?

Not if the user can leave it. Keeping focus inside an open modal is the expected pattern, as long as Esc or a visible Close button closes it and focus returns to where it came from.

Can I ask users to press a special key to exit?

Yes, if the component really needs Tab or the arrow keys for itself (a code editor, for example). Then the user must be told how to exit before they get stuck, not after.

Why didn't my scanner flag our keyboard trap?

Because scanners read markup at page load. A trap only happens when focus moves, so it takes someone (or something) pressing keys to find it.

Find out whether keyboard users can get out again.

Monitor real sessions for focus traps free, and run a journey that operates your key task keyboard-only.