2.1.2 No Keyboard Trap

Level: A | Principle: Operable | Since: WCAG 2.0 | Automation: Manual


What This Means

If keyboard focus can be moved to a component on the page, focus must also be movable away from that component using only the keyboard. If it requires more than standard navigation keys (Tab, Shift+Tab, Arrow keys, Escape), the user must be informed of the method to move focus away.

A keyboard trap makes the page completely unusable for keyboard-only users — they get stuck and cannot reach the rest of the content.

Who This Affects

  1. Keyboard-only users — become completely stuck, unable to navigate past the trap
  2. Screen reader users — cannot escape the trapped region to access other content
  3. Switch device users — rely on sequential navigation and have no way to break free from a trap

Common Pitfalls

1. Modal dialogs that trap focus without an exit

<!-- Bad: focus enters the modal but there is no way to close it via keyboard -->
<div class="modal" tabindex="-1">
  <p>You are trapped here forever.</p>
</div>

2. Embedded content (iframes) that capture focus

<!-- Bad: third-party widget captures focus with no escape -->
<iframe src="https://third-party-widget.com/chat"></iframe>

3. Custom widgets with event.preventDefault on Tab

// Bad: prevents Tab from moving focus out
input.addEventListener('keydown', (e) => {
  if (e.key === 'Tab') {
    e.preventDefault();
    // custom autocomplete logic
  }
});

How to Fix

Modal with proper focus management

<!-- Good: modal with close button and Escape key support -->
<div class="modal" role="dialog" aria-modal="true" aria-label="Confirm action">
  <p>Are you sure you want to proceed?</p>
  <button onclick="closeModal()">Cancel</button>
  <button onclick="confirm()">Confirm</button>
</div>
// Good: Escape closes the modal and returns focus
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && modalIsOpen) {
    closeModal();
    triggerButton.focus(); // return focus to the element that opened the modal
  }
});

Focus cycling within modals (not trapping)

// Good: cycle focus within modal, but allow Escape to exit
function trapFocusInModal(modal) {
  const focusable = modal.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
  const first = focusable[0];
  const last = focusable[focusable.length - 1];

  modal.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
      if (e.shiftKey && document.activeElement === first) {
        e.preventDefault();
        last.focus();
      } else if (!e.shiftKey && document.activeElement === last) {
        e.preventDefault();
        first.focus();
      }
    }
  });
}

How to Test

  1. Starting from the top of the page, press Tab through every interactive element, paying special attention to modals, iframes, media players, rich text editors, and autocomplete fields.
  2. At each element, verify you can press Tab or Shift+Tab to move away from it.
  3. When a modal or dialog opens, confirm you can close it with Escape and that focus returns to the element that triggered it.
  4. Test embedded third-party widgets (chat, video players, maps) to ensure Tab moves focus out of them.
  5. Pass: Focus can be moved away from every element using standard keyboard navigation (Tab, Shift+Tab, Escape).
  6. Fail: Focus gets stuck on any element with no keyboard method to escape, or a modal traps focus with no close mechanism.

axe-core Rules

No automated axe-core rules. This criterion requires manual testing.

Sources

  1. W3C WCAG 2.2 — Understanding 2.1.2
  2. W3C Technique G21: Ensuring that users are not trapped in content