A badly built modal is a closed door for a keyboard user: focus stays behind, ESC does nothing, and the screen reader keeps reading the page underneath. The 5 rules below are the direct application of the "dialog" pattern from the ARIA Authoring Practices Guide — the reference standard.
The 5 non-negotiable rules
- 01On opening, focus moves to the first focusable element of the modal (or to the modal itself)
- 02Focus is trapped in the modal: Tab and Shift+Tab loop inside, without escaping into the page
- 03The ESC key closes the modal
- 04On closing, focus returns to the element that opened the modal
- 05The attributes `role="dialog"` (or "alertdialog"), `aria-modal="true"` and `aria-labelledby` pointing to the title are present
The correct HTML skeleton
<div role="dialog" aria-modal="true" aria-labelledby="dlg-title">
<h2 id="dlg-title">Confirm the order</h2>
<button aria-label="Close">x</button>
<!-- content -->
</div>The `aria-labelledby` gives the dialog box a name: the screen reader announces "Confirm the order, dialog" on opening. The close button (often a cross) must carry an `aria-label="Close"`, otherwise it's announced as an unlabeled "button".
The focus trap, where it all plays out
While the modal is open, the rest of the page must be inert: neither focusable nor read. That's the role of focus trapping, and ideally of the `inert` attribute set on the background content. Without it, the keyboard user can "escape" the modal without seeing it and get lost in a page they shouldn't reach.
// Ready-made components
Reka UI (used by ComplAudit), Radix Vue, Headless UI: all these kits implement a compliant Dialog (focus, ESC, ARIA, inert) already tested. Writing your own modal by hand in 2026 means exposing yourself to missing at least 2 of the 5 rules — almost always the return focus and the trap.
Test it in 1 minute
- 01Open the modal by keyboard (Enter on the trigger)
- 02Check that focus has actually entered it
- 03Tab several times: focus must stay trapped in the modal
- 04Press ESC: it must close
- 05Check that focus has returned to the trigger button
Frequently asked questions
dialog or alertdialog?
`role="dialog"` for a standard box (form, information). `role="alertdialog"` for a critical interruption that demands an immediate decision ("Delete permanently?"). The alertdialog is announced more forcefully by the screen reader.
Is the native <dialog> HTML element enough?
The `<dialog>` element with `showModal()` natively handles the focus trap, the ESC key and background inertness. It's an excellent modern base, provided you take care of the `aria-labelledby` and return focus on close. Browser support is now wide.
Should I block the page scroll behind it?
Yes: it's a good UX and accessibility practice. The background must not scroll while the modal is open, on pain of disorienting the user.
Check that your modals handle focus and the keyboard properly:
→ Run an audit