Notifications & Dialogs
Dialog (Modal)
AvailableA dialog that renders in the foreground while making the background inert. Requires a focus trap and Escape key to close.
Specification updated
- Editorial#3412(opens in a new tab)Dialog Modal: Change sample heading level from H2 to H1
What Is a Modal Dialog?
A modal dialog is a small window that opens on top of the page andblocks interaction with the content behind it until it is closed. It is commonly used for settings panels, forms, and enlarged image views.
The hardest part of building a modal is focus management. Fortunately, using the native <dialog> element withshowModal() gives you all of that out of the box.
Why Does Accessibility Matter?
When a modal is not implemented correctly, these problems occur:
- Keyboard users press Tab and focusescapes through the dialog to invisible elements behind it (they get lost).
- Esc doesn't close the dialog, and after closing, focus jumps to an unexpected location.
- Screen reader users can still read the content behind the dialog, so they don't understand that they are "inside a dialog."
The native <dialog> + showModal() handles all of this automatically:focus trapping, Esc to close, making the backdrop inert, and restoring focus.
Live Demo (Recommended Implementation)
Press "Open settings" below to open the dialog. Try operating it with keyboard only — confirm that Tab doesn't escape the dialog and that Esc closes it.
Try it: Press Enter to open → Tab repeatedly and focus stays inside the dialog → Esc to close → Focus returns to the 'Open settings' button.
Tip
The moment the dialog opens, focus moves inside it (to the first button or an element with autofocus). When it closes, focus automatically returns to the trigger, so users never lose their place in the page.
Keyboard Interaction
| Key | Action | Priority |
|---|---|---|
| Esc | Close the dialog (handled natively with showModal()) | Required |
| Tab / Shift + Tab | Cycle focus within the dialog only (focus does not escape) | Required |
| Enter / Space | Activate the focused button | Required |
Note
All of the keyboard behaviors above come for free when you use <dialog>.showModal(). With a homemade div modal, you would have to implement each one manually in JavaScript.
Required WAI-ARIA Roles and Properties
| Target | Attribute / Role | Meaning |
|---|---|---|
| Dialog element | <dialog> element | Has implicit role="dialog". Opened as a modal via showModal(). |
| Dialog element | aria-labelledby="heading-id" or aria-label | Gives the dialog an accessible name (typically referencing the heading inside). |
| Dialog element | aria-describedby (optional) | Associates supplementary descriptive text. |
| Background content | (automatic) inert equivalent | showModal() automatically makes the background inert. Apply inert manually if not using showModal(). |
Implementation: Recommended Pattern (Good)
Good / Recommended
Use the native <dialog> with showModal(). Focus management, Esc handling, and backdrop inertness come built in.
Markup:
<button type="button" id="open">Open settings</button>
<dialog id="dialog">
<h2>Display Settings</h2>
<p>You can change the theme and font size here.</p>
<button type="button" id="close">Close</button>
</dialog>Open/close script (that's all you need):
const dialog = document.getElementById('dialog');
const openBtn = document.getElementById('open');
const closeBtn = document.getElementById('close');
// showModal() alone gives you focus trapping, Esc to close,
// and making the backdrop inert — all built into the platform.
openBtn.addEventListener('click', () => dialog.showModal());
closeBtn.addEventListener('click', () => dialog.close());
// After closing, the browser automatically returns focus to the trigger.Note
To give the dialog an accessible name, point <dialog aria-labelledby="title">to the id of the heading inside it. This way, a screen reader will announce "Display Settings, dialog" when it opens.
Anti-Pattern (Bad)
Below is a homemade modal built with a <div> overlay.You can open and close it with a mouse, but when you press Tabwhile it's open, focus escapes to the background. Esc won't close it either.
Try it: While the modal is open, press Tab repeatedly → focus moves to the 'Open settings' button and other elements behind the overlay. Esc does nothing. The × cannot be reached by keyboard.
<!-- ❌ A homemade modal using a div overlay -->
<button type="button" id="open">Open settings</button>
<div id="overlay" class="overlay" style="display:none">
<div class="modal">
<h2>Display Settings</h2>
<p>You can change the theme and font size here.</p>
<!-- span is not keyboard-accessible, so users can't close it -->
<span class="x" onclick="hide()">×</span>
</div>
</div>Bad / Avoid
Problems with this implementation:
- Tab escapes to the background — there is no focus trap, so focus moves to invisible elements behind the overlay.
- Esc doesn't close it — no keyboard event handler is implemented.
- Backdrop is not inert — screen readers can still read the content behind the modal.
- Close button is a
<span>— it cannot receive focus and is unreachable by keyboard. - No focus movement or restoration — opening doesn't move focus into the dialog, and closing doesn't return it to the trigger.
Tip
If you absolutely must use a <div>, you need to implement all of the following yourself: focus trapping, Esc handling, backdrop inert, focus restoration,role="dialog", and aria-modal="true". With <dialog>, most of that comes for free.
Implementation Checklist
- Uses the native <dialog> element opened with showModal()
- The dialog has an accessible name via aria-labelledby or aria-label
- Focus moves into the dialog when it opens
- Tab cycles within the dialog only (does not leak to the background)
- Escape closes the dialog
- Focus returns to the trigger element when the dialog closes
- Background content is inert (inoperable and hidden from assistive technology)
- Focus indicators are visible
Source (English):Dialog (Modal) Pattern — W3C APG(opens in a new tab)