Requires that when focus can move into a component with the keyboard, it can also move out again with the keyboard.
A keyboard user can tab into a component and then not get out again. They cannot go back and they cannot go forward. The only way out is a mouse, which they do not have, or reloading the page and losing their work.
This criterion requires that if keyboard focus can be moved to a component using a keyboard interface, then focus can also be moved away from it using only a keyboard interface. If this needs more than the arrow keys, Tab or other standard exit methods, the user must be told how.
Keyboard-only users and screen reader users who get stuck and must leave. People who use a switch or voice control. They may lose their work or be unable to continue.
The European Accessibility Act (Directive (EU) 2019/882) applies to in-scope products and services placed on the market after 28 June 2025. Recital 47 names four principles for websites and mobile applications: perceivable, operable, understandable and robust. A way out of every component is part of "operable".
The technical counterpart is the standard EN 301 549, which incorporates WCAG 2.1.2 by reference and turns it into separate clauses for each kind of ICT.
| Clause | Type of ICT | What it requires |
|---|---|---|
| 9.2.1.2 | Web pages | Web content must satisfy WCAG success criterion 2.1.2. |
| 10.2.1.2 | Non-web documents (PDF, Office files) | Documents must not trap keyboard focus where the standard applies the criterion to them. |
| 11.2.1.2 | Non-web software (native mobile apps) | Software must not trap keyboard focus. |
Whether and how the criterion applies to non-web documents is stated in clause 10 of the standard. Clause numbers follow the "9. + WCAG number" pattern of EN 301 549. In V3.2.1 the software clause is divided into further subclauses for open and closed functionality. Check the exact numbers in the standard when you cite them.
Standard version: EN 301 549 V4.1.1 (September 2026) is based on WCAG 2.2 and is the first version prepared specifically for the EAA. It has not yet been cited in the Official Journal of the European Union, so for now it is best treated as a voluntary technical route to the EAA's requirements. Once cited, it will give a presumption of conformity. The text of criterion 2.1.2 is the same in WCAG 2.1 and 2.2.
A note on EAA scope: Article 2(4) of the Directive excludes certain content from the requirements, for example office file formats published before 28 June 2025 and archives that are not updated after that date. Content you publish or change after that date is not covered by these exclusions.
Directive (EU) 2019/882, EUR-Lex, opens in a new tab
Preventing the default Tab behavior with no exit leaves the user stuck.
editor.addEventListener("keydown", e => {
if (e.key === "Tab") e.preventDefault(); // inserts a tab, no way out
});editor.addEventListener("keydown", e => {
if (e.key === "Escape") { editor.dataset.trap = "off"; } // Esc, then Tab leaves
if (e.key === "Tab" && editor.dataset.trap !== "off") {
e.preventDefault(); insertTab();
}
});
// Document it: "Press Escape, then Tab, to leave the editor."A native dialog keeps focus inside while it is open and closes with Escape.
<div class="modal">…</div> <!-- focus wraps and no close key --><dialog id="settings" aria-labelledby="t">
<h2 id="t">Settings</h2>
…
<button type="button" onclick="this.closest('dialog').close()">Close</button>
</dialog>
<script>settings.showModal(); // Escape closes it</script>Third-party players and widgets can trap focus. Test them and pick ones that do not.
<iframe src="https://widget.example/chat" title="Chat"></iframe> <!-- traps Tab --><iframe src="https://widget.example/chat" title="Chat"></iframe>
<!-- After testing: Tab enters and leaves; if not, change or replace the widget -->A script that returns focus to the first element on every Tab loops for ever.
last.addEventListener("keydown", e => {
if (e.key === "Tab" && !e.shiftKey) { e.preventDefault(); first.focus(); }
});// Loop only inside an open modal dialog that has a close button and responds to EscapeWhen a non-modal panel opens, let users continue with Tab out of it and close it with Escape.
panel.addEventListener("focusout", () => panel.querySelector("input").focus());panel.addEventListener("keydown", e => {
if (e.key === "Escape") { panel.hidden = true; trigger.focus(); }
});Make the page behind a modal inert so focus stays where it should.
<main>…</main> <!-- still tabbable behind the dialog --><main inert>…</main> <!-- while the dialog is open -->Native apps can trap focus in custom views and overlays. Make sure every overlay can be dismissed with a keyboard or switch.
import SwiftUI
struct SettingsSheet: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
NavigationStack {
Form { /* controls */ }
.navigationTitle("Settings")
.toolbar {
// A visible close control that works with keyboard and VoiceOver
ToolbarItem(placement: .confirmationAction) {
Button("Done") { dismiss() }
}
}
}
// Standard sheets also dismiss with Escape on a hardware keyboard
}
}import androidx.compose.material3.*
import androidx.compose.runtime.Composable
@Composable
fun SettingsDialog(onDismiss: () -> Unit) {
// AlertDialog closes with Back/Escape and keeps focus inside while open
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Settings") },
text = { /* controls */ },
confirmButton = {
TextButton(onClick = onDismiss) { Text("Done") }
}
)
}