Modal dialog semantics

The markup that makes a popup a real dialog: dialog role, a name, focus moved in and trapped, Escape to close, focus returned.

making the popup accessibleproper markup for a dialogaccessible modalrole dialog aria-modalhow to code a modal properlydialog a11yscreen reader friendly popuparia-modal true

See it

Live demo coming soon

What it is

A div with a drop shadow and a dark overlay looks like a dialog and is not one. The contract assistive tech expects: role='dialog' (or the native dialog element), aria-modal='true' to signal that the rest of the page is out of scope, an accessible name from aria-labelledby pointing at the dialog title, focus moved inside on open, focus trapped while open, Escape to close, and focus returned to the trigger afterwards. A dialog whose message is urgent enough to demand an answer right now can use role='alertdialog' with aria-describedby on the message, but that is the exception, not the default for anything that happens to be scary.

Reach for it whenever you block the page: destructive-action confirmations, auth prompts, media lightboxes, anything that demands an answer before the user continues. Surfaces that do not block (popovers, dropdown menus, tooltips, toasts) are not dialogs and should never claim aria-modal.

Gotcha: aria-modal='true' is a signal, not an enforcement mechanism. It tells assistive tech to treat the rest of the page as out of scope; it does not make anything inert, support varies by reader, and the keyboard ignores it completely. Tab still walks out the back unless you add inert or a real focus trap. Which is the case for the native dialog element with showModal(): it gives you the top layer, a backdrop, background inertness, and Escape handling for free, and ends up shorter than the ARIA version.

Ask AI for it

Rebuild this popup as an accessible modal dialog. Use the native dialog element with showModal(), or fall back to role='dialog' plus aria-modal='true' with the rest of the page marked inert. Give it an accessible name with aria-labelledby pointing at the dialog title, and aria-describedby pointing at the body text for confirmations. On open, move focus to the first meaningful control inside; trap Tab within the dialog; close on Escape and on a visible close button; restore focus to the element that opened it. Keep role='dialog' as the default and reach for role='alertdialog' only when the dialog exists to deliver an urgent message that needs an answer immediately, not for every destructive confirmation. Prevent background scrolling while open, and make sure clicking the backdrop is not the only way to dismiss it.

You might have meant

focus trapfocus managementaccessible namekeyboard navigationaria hidden

Go deeper