2.1.2 Level A Guideline 2.1 Keyboard Accessible Added in WCAG 2.0 Not detectable automatically

No Keyboard Trap

If focus can be moved to a component using the keyboard, it can be moved away again using only the keyboard. If a non-standard key is needed to exit, the user must be told.

Who this affects

Keyboard-only users. A trap is catastrophic rather than merely inconvenient — the only escape is to close the tab and lose all work.

The failure and the fix

Each example below is a self-contained page, loaded in a frame so its markup cannot affect this page. Open either one on its own to test it with a keyboard or screen reader.

Fails 2.1.2

Open the failing example in a new tab

fail.html — the problem

<h1>Newsletter</h1>
<p><a href="#before">A link before the dialog</a></p>

<!-- FAILURE: this focus trap has no exit. It cycles focus inside the dialog
     forever: Escape is not handled, there is no close button, and the trap
     runs even though the dialog is meant to be dismissible.

     A trap is the most severe keyboard failure there is. The user's only
     remaining option is to close the browser tab and lose their work. -->
<div class="modal" id="dlg">
  <h2>Before you go…</h2>
  <div class="field">
    <label for="em">Email address</label>
    <input type="email" id="em">
  </div>
  <button type="button" class="button" id="sub">Subscribe</button>
</div>

<p><a href="#after">A link after the dialog</a></p>
<p id="before">Before.</p><p id="after">After.</p>

Meets 2.1.2

Open the passing example in a new tab

pass.html — the fix

<h1>Newsletter</h1>
<p><a href="#before">A link before the dialog</a></p>

<!-- FIX: a modal SHOULD hold focus while it is open - that part is correct and
     expected. What makes it lawful rather than a trap is that there is always
     a way out using only the keyboard:
       - Escape closes it
       - a visible Close button is in the cycle
       - focus returns to the element that opened it
     If a component genuinely needs an unusual key to exit, 2.1.2 requires you
     to tell the user what that key is, at the point they need to know. -->
<div class="modal" id="dlg" role="dialog" aria-modal="true" aria-labelledby="dlg-h">
  <h2 id="dlg-h" tabindex="-1">Before you go…</h2>
  <p id="dlg-hint">Press <kbd>Escape</kbd> to close this dialog.</p>
  <div class="field">
    <label for="em">Email address</label>
    <input type="email" id="em" autocomplete="email">
  </div>
  <button type="button" class="button" id="sub">Subscribe</button>
  <button type="button" class="button button--secondary" id="close">Close</button>
</div>

<p><a href="#after">A link after the dialog</a></p>
<p id="before">Before.</p><p id="after">After.</p>

How to test it

Tab into every widget — modals, custom media players, embedded editors, third-party iframes — and confirm you can Tab or Escape back out. Test in both directions with Tab and Shift+Tab.

  • Automated

    Detecting a trap requires actually driving focus through the page and observing that it cannot leave.

    No axe rule maps to this criterion. It has to be checked by a person.

  • Keyboard

    Unplug the mouse. Move through the page with Tab, Shift+Tab, Enter, Space and the arrow keys. See the keyboard testing script.

  • Screen reader

    Listen to both examples with NVDA, JAWS, VoiceOver or TalkBack and compare what is announced. See the screen reader cheat sheets.

  • Visual

    Zoom to 200% and 400%, narrow the viewport to 320 px, and apply the text-spacing overrides. See the visual testing procedures.

In the specification