WCAG 2.1.2: No Keyboard Trap
A keyboard user should be able to move into any component and, just as importantly, move back out of it using the keyboard alone. When focus gets stuck in a widget, a modal, or an embedded object with no keyboard way out, the entire page freezes for that user — often the only escape is to reload and lose their work. This criterion exists to make sure that never happens.
The success criterion, in full
If keyboard focus can be moved to a component of the page using a keyboard interface, then focus can be moved away from that component using only a keyboard interface, and, if it requires more than unmodified arrow or tab keys or other standard exit methods, the user is advised of the method for moving focus away.
The rule has two halves. First, focus that can go in must be able to come outusing only the keyboard. Second, if the exit needs anything beyond the standard Tab, Shift+Tab, arrow keys, or Escape, the user must be told how — the “advisory” clause. The easiest way to comply is to support the standard exits so no special instruction is ever needed.
Who this helps
A keyboard trap does not just inconvenience one group — it can lock the entire page for anyone who does not, or cannot, use a mouse. When focus cannot escape, there is often no recovery short of reloading the page.
Keyboard-only users
People who navigate entirely with Tab, Shift+Tab, and arrow keys have no pointer to click their way out. A trap means the rest of the page is unreachable until they reload.
Users with motor disabilities
Those using switch devices, sip-and-puff controls, or head pointers rely on sequential keyboard-style navigation. A single trapped widget can halt an entire session.
Screen reader users
Screen readers drive the page through the keyboard. If focus is trapped, the reader's virtual cursor is stuck too, cutting the user off from everything past the trap.
Voice control users
Speech-driven navigation issues keyboard-equivalent commands. When those commands cannot move focus out of a component, the user is stranded just the same.
Power users and everyone in a hurry
Keyboard shortcuts are faster for many sighted users too. A trap that forces them back to the mouse — or to reload — breaks their flow and their trust in the interface.
Anyone whose mouse fails
A dead trackpad or an unresponsive mouse turns every user into a keyboard-only user. Escapable components keep the page usable when the pointer is unavailable.
What the requirement covers
The criterion is best remembered as one sentence: you may move focus in, but you must always be able to move focus out using the keyboard alone. “Standard exit methods” are the keys users already expect — Tab and Shift+Tab to leave in either direction, and very often Escape to dismiss a layer. If those work everywhere, you pass with nothing more to do.
- Focus can move in. It is fine — and normal — for keyboard focus to enter a component: a menu, a dialog, a date picker, an editor, an embedded player.
- Focus must move out. From wherever focus lands inside that component, the user must be able to move it away again using only the keyboard, without resorting to a mouse.
- Standard exits should just work. Tab and Shift+Tab should carry focus out of the component in the natural reading order; for overlays, Escape should dismiss them and return focus to a sensible place.
- Non-standard exits must be advertised. If — and only if — leaving requires something other than Tab, Shift+Tab, arrow keys, or Escape, the component must tell the user which key to press to get out.
Focus containment vs. a keyboard trap
There is an important distinction that trips people up. Deliberately containing focus inside an open modal dialog — so Tab cycles through the dialog's own controls instead of reaching the page behind it — is the recommended pattern for modals, and it is perfectly compliant as long as the user can leave the dialog with the keyboard. Provide Escape and a keyboard-reachable Close button, and the containment is a feature, not a trap. Remove every keyboard exit and the same code becomes a violation. The test is always the same: can the user get out with the keyboard alone?
How it relates to nearby criteria
2.1.2 sits alongside 2.1.1 Keyboard, which is about being able to operate functionality with the keyboard at all, and 2.4.3 Focus Order, which is about focus moving in a sensible sequence. 2.1.2 is narrower and specific: it is only about being able to leave a component you have entered.
Pass and fail examples
✓ Passes 2.1.2
- A modal dialog that contains focus but closes on Escape and has a reachable Close button.
- A dropdown menu that closes and returns focus to its trigger when the user presses Escape.
- A date picker you can Tab into and Tab (or Shift+Tab) straight back out of.
- A rich text editor that requires a documented key to exit — and shows that instruction to the user.
- An embedded video player whose controls you can Tab through and then Tab past to the rest of the page.
✗ Fails 2.1.2
- A modal that loops Tab internally with no Escape handler and no keyboard-reachable close.
- A custom widget whose keydown handler calls preventDefault on Tab without offering any exit.
- An embedded third-party widget or iframe that captures focus and never releases it.
- A field that calls focus() on its own blur to force focus back on a validation error.
- A component that needs a non-standard key to leave but never tells the user which key.
Code examples
The trap vs. the escapable modal
The failure is almost always a keydown handler that blocks the exit keys. The fix is to let Escape close the layer and let Tab wrap within the dialog while a Close button provides an obvious way out.
<!-- ✗ Trap: Escape is blocked, Tab is preventDefaulted, no way out -->
<div class="modal" role="dialog">
<input type="text" placeholder="You're stuck here" />
<button>Can't escape</button>
</div>
<script>
modal.addEventListener('keydown', (e) => {
if (e.key === 'Escape') e.preventDefault() // blocks the exit
if (e.key === 'Tab') e.preventDefault() // no way forward or back
})
</script>
<!-- ✓ Compliant: focus is contained but Escape and Close both leave -->
<div class="modal" role="dialog" aria-modal="true" aria-labelledby="t">
<h2 id="t">Settings</h2>
<button class="close" aria-label="Close dialog">×</button>
<input type="text" />
<button type="submit">Save</button>
</div>A reusable focus-trap that still lets users out
A good focus trap only wraps Tab at the boundaries of the dialog; it never intercepts Escape, and it restores focus to the element that opened the dialog when it closes.
function trapFocus(container, onClose) {
const previouslyFocused = document.activeElement
const focusables = container.querySelectorAll(
'a[href], button:not([disabled]), input:not([disabled]),' +
'select, textarea, [tabindex]:not([tabindex="-1"])'
)
const first = focusables[0]
const last = focusables[focusables.length - 1]
first?.focus()
container.addEventListener('keydown', (e) => {
// Escape ALWAYS leaves — never block it
if (e.key === 'Escape') { onClose(); return }
if (e.key === 'Tab') {
// Only wrap AT the edges; normal Tab moves freely inside
if (e.shiftKey && document.activeElement === first) {
e.preventDefault(); last.focus()
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault(); first.focus()
}
}
})
return () => previouslyFocused?.focus() // restore focus on close
}Advising the user of a non-standard exit
If a component genuinely needs a special key to leave — some embedded editors capture Tab to insert indentation — the criterion still passes as long as you tell the user how to get out, both visibly and to assistive technology.
<div role="group" aria-labelledby="editor-label"
aria-describedby="editor-exit">
<span id="editor-label">Code editor</span>
<textarea aria-describedby="editor-exit"></textarea>
<!-- The advisory the criterion requires -->
<p id="editor-exit">
Tab inserts a tab character. Press Escape, then Tab,
to move focus out of the editor.
</p>
</div>Interactive demo
Try both widgets below with your keyboard. The trapped one blocks Tab and Escape so focus cannot leave — a genuine 2.1.2 failure — while the accessible one contains focus for usability but lets you press Esc or use the Close button to get out. A Release control outside the trap is always available so the real page never actually locks up.
Live status
No widget open. Open one and try to Tab or press Esc to leave it.
The “Release trap” and “Reset” controls live outside the widgets, so the demo can never truly lock your keyboard.
Keyboard trap (fails 2.1.2)
This widget preventDefaults Tab and Escape, looping focus inside forever. There is no advertised way out.
Accessible modal (passes 2.1.2)
Focus is contained for usability, but Escape or the Close button always returns you to the page.
Common failures
- A modal or overlay that loops focus internally with no Escape handler and no keyboard-reachable Close button.
- A custom widget whose keydown listener calls preventDefault on Tab without providing any alternative exit.
- Calling element.focus() on blur to force focus back — for example on a validation error — so the user can never leave the field.
- Embedded third-party content (legacy Flash, some PDF/plugin embeds, or an iframe widget) that captures the keyboard and never releases it.
- Dropdown menus, date pickers, and rich text editors that swallow Tab and Escape, stranding focus inside them.
- Requiring a non-standard key to exit a component without ever advising the user which key to press.
- A media player whose controls trap focus so Tab never reaches the content after the player.
- Blocking the Escape key globally 'to prevent accidental closes', which removes the standard exit from every layer at once.
How to test for 2.1.2
- 1
Put the mouse away entirely
Unplug or ignore the pointer and drive the whole page with Tab, Shift+Tab, arrow keys, and Escape. Testing keyboard traps only works if you truly cannot fall back to clicking.
- 2
Tab into and out of every component
Reach each menu, dialog, widget, embed, and media player with Tab, then confirm Tab and Shift+Tab carry focus back out again. If focus never leaves, you have found a trap.
- 3
Open every modal and try Escape and Close
For each overlay, confirm that Escape dismisses it and that a Close button is reachable by keyboard and returns focus somewhere sensible on the page.
- 4
Exercise embedded and third-party content
Tab into iframes, video players, date pickers, and any plugin or widget you did not build. These are the most common traps because their exit behaviour is outside your code.
- 5
Watch for forced focus and blocked keys
Look for fields that snap focus back on blur (e.g. on validation) and for handlers that preventDefault on Tab or Escape. Both silently remove the standard exit.
- 6
Verify any non-standard exit is advertised
If a component legitimately needs a special key to leave, confirm the instruction is actually presented to the user — visibly and via an accessible description — not just buried in documentation.
Automated tools such as axe DevTools, Pa11y, and Lighthouse can flag some suspicious focus patterns, but keyboard traps are fundamentally a manual test — a human pressing Tab is the only reliable check. For a structured audit, work through the full WCAG 2.2 checklist.
Related Success Criteria
All functionality is available from a keyboard interface.
All functionality is available from a keyboard interface without exception.
Single character key shortcuts can be turned off or remapped.
Users can turn off, adjust, or extend time limits.
Moving, blinking, or auto-updating content can be paused, stopped, or hidden.
Frequently asked questions
What does WCAG 2.1.2 No Keyboard Trap require?
It requires that if keyboard focus can be moved to a component using a keyboard interface, then focus can also be moved away from that component using only the keyboard. Standard exit methods are the Tab and Shift+Tab keys (and frequently Escape). If leaving a component needs something other than unmodified arrow keys, Tab, or another standard exit method, the user must be advised how to move focus away. It is a Level A criterion introduced in WCAG 2.0 under Guideline 2.1 Keyboard Accessible.
Is trapping focus inside a modal dialog a violation of 2.1.2?
Not by itself. Deliberately containing focus within an open modal — so that Tab cycles among the dialog's controls rather than reaching the page behind it — is an accepted, recommended pattern. It only becomes a keyboard trap if the user cannot leave the dialog with the keyboard alone. A compliant modal lets the user press Escape or Tab to a reachable Close button to dismiss it and return focus to the page. A modal that swallows Escape and offers no keyboard-reachable way out is the failure.
What is the 'advisory' part of the requirement?
The criterion allows a component to require a non-standard key to exit — but only if the user is told how. If pressing Escape or Tab does not move focus away and the user must instead press, say, F6 or Ctrl+Alt+Right, that instruction must be provided to the user (for example, as visible text or an accessible description inside the component). If a non-standard exit is required and no advisory is given, the criterion fails. In practice, the simplest way to pass is to support the standard Tab/Shift+Tab (and Escape) exits so no advisory is needed at all.
How is 2.1.2 different from 2.1.1 Keyboard?
2.1.1 Keyboard is about whether functionality can be operated with the keyboard at all — can you reach and activate a control without a mouse. 2.1.2 No Keyboard Trap is specifically about being able to leave: once your focus is on a component, you must be able to move it away again using the keyboard. A widget can satisfy 2.1.1 (you can operate it) yet fail 2.1.2 (once inside, Tab never gets you out). They are related but distinct, and both are Level A.
What historically caused keyboard traps?
Embedded third-party content is the classic culprit: legacy Flash objects, some PDF and plugin embeds, and iframes for widgets that captured the keyboard and never released it. Modern equivalents are custom JavaScript widgets — date pickers, rich text editors, menus, and JS modals — that intercept the Tab key with preventDefault and loop focus internally without offering Escape or any advertised exit. Any control that calls element.focus() on blur to force focus back to itself will also create a trap.
Does 2.1.2 apply to embedded iframes and media players?
Yes. If keyboard focus can enter embedded content — an iframe, a video player, a third-party widget — the user must be able to move focus back out with the keyboard. This is one of the hardest cases to guarantee because the embedded content may be outside your direct control. You must test that Tab enters and, crucially, exits the embed. If an embedded object is known to trap focus and cannot be fixed, it should not be included, because there is no keyboard-only way to escape it.