Skip to main content
Frontend

Building Accessible Dialogs From Scratch

8 min read

Dialog overlays are one of the most common user interface patterns, yet they are frequently built with severe accessibility flaws. When screen reader users or keyboard-only navigators encounter an inaccessible modal, they often get trapped or lose context entirely.

Vocabulary

Term Meaning
Focus trap Keeps keyboard focus inside the open dialog
Focus restoration Returns focus to the trigger element when the dialog closes
WCAG 2.2 AA Web accessibility standard this pattern targets
aria-modal Tells assistive tech the background is inert
aria-labelledby Links the dialog to its visible title for screen readers

Steps

Step 1 — Meet the WCAG checklist

To meet WCAG 2.2 AA standards, an interactive dialog must implement:

  • Focus Trapping: Keyboard focus must remain confined inside the active dialog. Users should not be able to tab back into the background document.
  • Focus Restoration: When the dialog closes, focus must return to the trigger element that launched it.
  • Esc Key Closing: Pressing the Escape key must close the active dialog immediately.
  • ARIA Attributes: The modal container requires role="dialog", aria-modal="true", and must be labelled by headings via aria-labelledby.

Step 2 — Implement a focus trap in vanilla TypeScript

Here is a clean implementation of a focus trap utility. It queries all focusable child elements in the dialog, tracks the first and last items, and loops the focus during keyboard tabbing.

typescript

Common pitfalls

  • Opening a dialog without moving focus inside — screen reader users lose context
  • Forgetting focus restoration — keyboard users land nowhere after close
  • Missing aria-labelledby — dialog purpose is unclear to assistive tech
  • Trap without Escape handler — users feel stuck

Verify it works

Open the dialog with keyboard only. Tab through all controls — focus should never leave the dialog. Press Escape — dialog closes and focus returns to the trigger. Run an axe audit — no critical violations on the modal.

Takeaway

Focus trap, Escape to close, and focus restoration are non-negotiable for accessible dialogs. Primitives like Radix UI encode this for you — but knowing the mechanics helps you debug when something breaks.