Dialog and Modal
Style native dialogs with accessible browser behavior, plus a compatibility surface for application-managed overlays.
Native dialog structure before behavior
The open state exposes the styled surface while headings and method=dialog controls preserve native semantics.
<dialog data-modal aria-labelledby="archive-title" id="archive-dialog">
<h2 id="archive-title">Archive project?</h2>
<p>You can restore it later from settings.</p>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Archive</button>
</form>
</dialog>
<script>
archiveDialog.showModal();
</script> More examples
Each example introduces a different part of the shipped API.
Theme the surface without replacing dialog behavior
Component tokens control width, surface, border, radius, and backdrop while the browser retains focus and Escape handling.
dialog[data-modal] {
--modal-width: 28rem;
--modal-radius: var(--radius-xl);
--modal-border-color: var(--accent);
--modal-backdrop-blur: 4px;
} --modal-width: 28rem--modal-radius: var(--radius-xl)--modal-backdrop-blur: 4pxUse a native <dialog> whenever the user must respond to content in an overlay. CSS Tags styles dialog[data-modal] and dialog.modal; the browser provides the top layer, Escape handling, focus placement, and return focus when showModal() is used.
Native dialog
The browser supplies focus management, Escape handling, and the top layer.
<button type="button" data-open-dialog="dialog-example-7ucscp6c5bn">Review changes</button>
<dialog id="dialog-example-7ucscp6c5bn" data-modal aria-labelledby="dialog-example-7ucscp6c5bn-title">
<h3 id="dialog-example-7ucscp6c5bn-title">Publish these changes?</h3>
<p>The updated documentation will be visible to everyone.</p>
<form method="dialog">
<layout-cluster gap="var(--space-sm)">
<button value="cancel">Keep editing</button>
<button value="confirm">Publish</button>
</layout-cluster>
</form>
</dialog> Recommended markup
<button type="button" id="open-publish">Review changes</button>
<dialog data-modal aria-labelledby="publish-title" id="publish-dialog"> <h2 id="publish-title">Publish these changes?</h2> <p>The updated documentation will be visible to everyone.</p>
<form method="dialog"> <button value="cancel">Keep editing</button> <button value="confirm">Publish</button> </form></dialog>
<script> const dialog = document.querySelector('#publish-dialog'); document.querySelector('#open-publish').addEventListener('click', () => { dialog.showModal(); });</script>method="dialog" closes the dialog without requiring a custom close handler. Read dialog.returnValue from the close event when the selected action matters.
Supported hosts
| Host | Use |
|---|---|
<dialog data-modal> | Recommended semantic API |
<dialog class="modal"> | Class-based native dialog |
<modal-dialog> | Visual host only; behavior and semantics are your responsibility |
[data-modal-overlay] / .modal-overlay | Compatibility hook for an application-managed overlay |
The compatibility overlay expects a child with [data-modal-panel] or .modal__panel. Prefer native <dialog> for new work.
Theme tokens
:root { --modal-width: 42rem; --modal-padding: var(--space-xl); --modal-bg: var(--surface-default); --modal-text: var(--text-default); --modal-border-color: var(--outline-subtle); --modal-radius: var(--radius-lg); --modal-backdrop: rgb(8 12 20 / 0.68); --modal-backdrop-blur: 4px;}Accessibility checklist
- Give the dialog an accessible name with
aria-labelledbyoraria-label. - Call
showModal(), not onlyshow(), when background content must be inert. - Put the least destructive action first and avoid focusing a destructive action automatically.
- Do not add
role="dialog"to native<dialog>; it already has the correct semantics. - Keep essential instructions in visible text rather than relying on the backdrop.