Live demo: Open the dialogs (modals) micro-demo
What goes wrong
- No name: assistive tech may announce “dialog” with no context.
- Focus stays in the background: the user keeps tabbing and activates controls they can’t see.
- No focus trap: focus escapes the modal, then it’s hard to find the “real” task again.
- No Escape / close control: users feel stuck.
Small, reliable pattern
- Semantics:
role="dialog"and a name viaaria-labelledby(oraria-label). - Modal: add
aria-modal="true"and prevent background interaction while open. - On open: move focus into the dialog (close button or first input).
- While open: trap
Tab/Shift+Tabinside. - On close: restore focus to the control that opened the dialog.
Minimal markup
<button id="openDialog">Edit profile</button>
<div
role="dialog"
aria-modal="true"
aria-labelledby="dlgTitle"
aria-describedby="dlgDesc"
hidden
>
<h2 id="dlgTitle">Edit profile</h2>
<p id="dlgDesc">Update your display name.</p>
<label>Display name<input /></label>
<button type="button" id="closeDialog">Close</button>
</div>
Keyboard expectations
Tab/Shift+Tabstay within the dialog.Esccloses (if safe); provide a visible close button too.- Clicking the backdrop can close if it doesn’t cause surprise data loss.
Notes
- Prevent background interaction. Where supported,
inertis a good tool; otherwise manage focus + pointer events. - If closing could discard data, confirm before closing (or avoid closing on Escape/backdrop).