Dialog
A modal built on native <dialog> — focus trapping, Escape and the backdrop come from the browser; header, body and footer slots.
Examples
Confirmation dialog
HTML for “Confirmation dialog”
<button class="mp-button" type="button" data-variant="secondary" commandfor="dialog-publish" command="show-modal">Publish workshop…</button>
<dialog class="mp-dialog" id="dialog-publish" data-size="sm" aria-labelledby="dialog-publish-title" closedby="any">
<div class="mp-dialog__header">
<h2 class="mp-dialog__title" id="dialog-publish-title">Publish this workshop?</h2>
<button class="mp-button" type="button" data-variant="ghost" data-size="sm" data-icon-only aria-label="Close" commandfor="dialog-publish" command="close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
</button>
</div>
<div class="mp-dialog__body">
<p>It becomes visible to all 3,214 educators in the community, and they'll get an email invitation.</p>
</div>
<div class="mp-dialog__footer">
<button class="mp-button" type="button" data-variant="ghost" commandfor="dialog-publish" command="close">Not yet</button>
<button class="mp-button" type="button" commandfor="dialog-publish" command="close">Publish</button>
</div>
</dialog>
Large reading dialog
HTML for “Large reading dialog”
<button class="mp-button" type="button" data-variant="ghost" commandfor="dialog-syllabus" command="show-modal">Read the syllabus</button>
<dialog class="mp-dialog" id="dialog-syllabus" data-size="lg" aria-labelledby="dialog-syllabus-title" closedby="any">
<div class="mp-dialog__header">
<h2 class="mp-dialog__title" id="dialog-syllabus-title">AI Literacy I — syllabus</h2>
<button class="mp-button" type="button" data-variant="ghost" data-size="sm" data-icon-only aria-label="Close" commandfor="dialog-syllabus" command="close">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg>
</button>
</div>
<div class="mp-dialog__body mp-prose">
<h3>Week 1 — Start from the classroom</h3>
<p>Bring one real problem. We map it before we touch a tool.</p>
<h3>Week 2 — How models actually work</h3>
<p>Tokens, context, and why a confident answer can still be wrong.</p>
</div>
<div class="mp-dialog__footer">
<button class="mp-button" type="button" commandfor="dialog-syllabus" command="close">Done</button>
</div>
</dialog>
Guidance
The native <dialog> element opened with showModal() traps focus, makes the page behind it inert, closes on Escape and restores focus on close. The component adds the elevated surface (--color-bg-elevated, which lightens in dark mode), the blurred scrim, and an entrance transition.
Opening and closing
- Invoker attributes (no script):
<button commandfor="my-dialog" command="show-modal">andcommand="close"inside.components/membrane.jspolyfills these where the browser lacks them. - Script:
document.getElementById('my-dialog').showModal(). - Form: a
<form method="dialog">inside closes it on submit. closedby="any"also closes on a backdrop click (polyfilled bymembrane.js).
Accessibility
- Always open with
showModal()(orcommand="show-modal"), never by toggling theopenattribute — only the modal path traps focus and makes the page inert. - Label it with
aria-labelledbypointing at the title. - Put focus somewhere sensible: by default the first focusable element; add
autofocusto the least destructive button for confirmations. - The page behind an open modal stops scrolling.
Manual screen-reader and usability checks have not been recorded for this inventory. See the accessibility evidence for automated coverage, remaining checks and the result-recording protocol.
API
| Class | Kind |
|---|---|
.mp-dialog | root |
.mp-dialog__header | part of .mp-dialog |
.mp-dialog__title | part of .mp-dialog |
.mp-dialog__body | part of .mp-dialog |
.mp-dialog__footer | part of .mp-dialog |
| Attribute | Values |
|---|---|
data-size | sm · lg |
States styled: :modal [open]. Set the native attribute or ARIA state; there are no state classes.
| Token | Default | Light · dark |
|---|---|---|
--mp-dialog-width | 35rem | — |
--mp-dialog-bg | var(--color-bg-elevated) | #FAF6EE · #27272D |
--mp-dialog-fg | var(--color-text-primary) | #2A2622 · #F0EBE1 |
--mp-dialog-body | var(--color-text-secondary) | #5C564E · #A39D93 |
Design tokens consumed: --blur-backdrop --border-default --color-bg-elevated --color-overlay-bg --color-text-primary --color-text-secondary --duration-fast --duration-normal --elevation-modal --font-sans --font-serif --leading-body --leading-subheading --radius-xl --space-2 --space-3 --space-4 --space-6 --text-body --text-h3 --tracking-subheading --transition-enter --transition-exit --weight-semibold
Verified contrast
The build measures each pair for the defaults and for every tone, variant and state that reassigns a token, over page and card surfaces. The ratios below are the worst case in each mode; a pair below its minimum stops the build.
| Use | Pair | Light | Dark | Minimum |
|---|---|---|---|---|
| dialog title | --mp-dialog-fg on --mp-dialog-bg | 13.93 | 12.50 | 7.0 |
| dialog body | --mp-dialog-body on --mp-dialog-bg | 6.73 | 5.52 | 4.5 |