Dialog

The native dialog element as a modal, opened by a button that names it, with the page behind it inert and focus held inside.

Example

View Code
<button class="button" type="button" data-open="delete-project" data-variant="alert">Delete project</button>

<dialog class="dialog" id="delete-project" aria-labelledby="delete-project-title">
    <h2 id="delete-project-title">Delete this project?</h2>
    <p>Everything in it goes too, and this cannot be undone.</p>
    <footer>
        <form method="dialog"><button class="button" type="submit" data-emphasis="medium">Cancel</button></form>
        <button class="button" type="button" data-variant="alert">Delete</button>
    </footer>
</dialog>

When to use it

A decision that has to be made before anything else happens: confirming something destructive, a short form the page cannot carry, a message that must be acknowledged. Anything the reader can come back to later belongs on the page rather than over it.

How it works

A native dialog, opened as a modal. That one word is why the component exists in this shape: showModal makes everything behind the dialog inert, keeps focus inside it, closes it on Escape, and gives you a ::backdrop to paint. None of that is Yeti's, and none of it needs ARIA.

Opening is the one thing the browser will not do for you, so dialog.js does it: any button carrying data-open with a dialog's id opens it, a click on the backdrop closes it, and focus returns to the button that opened it. Closing needs no script at all if you use a form: a button inside <form method="dialog"> closes the dialog on its own.

<button class="button" type="button" data-open="share">Share</button>

<dialog class="dialog" id="share" data-width="sm" aria-labelledby="share-title">
    <h2 id="share-title">Share this page</h2>
    <p>Anyone with the link can read it.</p>
    <footer>
        <form method="dialog"><button class="button" type="submit">Done</button></form>
    </footer>
</dialog>

Accessibility

Name the dialog with aria-labelledby pointing at its heading, so it is announced as something rather than as an unnamed dialog. Because it is opened modally the page behind it is genuinely inert, not merely covered, so a screen reader cannot wander out of it. Focus returns to the opener on close, which is what keeps a keyboard reader's place.

Without the module the dialog never opens. That is a real limitation, not a detail: do not put the only way to reach something behind a dialog on a page that does not load dialog.js.

Attributes

Attribute Type Values Default Description
data-width enum 2xs, xs, sm, md, lg, xl, 2xl md How wide the dialog may grow.

Children

  • > *: at least 1. The contents: a heading that names the dialog, then whatever it is for.
  • > footer: 0 to 1. The buttons, at the end edge. A button inside a form with method="dialog" closes it with no script.

Tokens

Token Description
--yeti-dialog-surface Background of the dialog.
--yeti-dialog-radius Corner of the dialog.
--yeti-dialog-padding Padding inside.
--yeti-dialog-backdrop The wash over the page behind it.
--yeti-shadow-md The dialog's shadow.
--yeti-space-md The least margin kept between the dialog and the viewport's edges.
--yeti-color-text The dialog's text.
--yeti-width-md The default width, when data-width is absent.
--yeti-space-sm Gap between the footer's actions.
--yeti-duration-fast How long the dialog and its backdrop take to appear.
--yeti-ease The curve of that transition.
Internal tokens (may change between minor versions) - `--_yeti-width`

Accessibility

  • Give the dialog a name with aria-labelledby pointing at its heading. Opened with showModal, which is what dialog.js does, the browser makes everything behind it inert, holds focus inside, and closes on Escape; none of that needs ARIA. A button inside a form with method="dialog" closes it without any script. Without the module nothing opens the dialog, so never put the only route to something behind one on a page that does not load it.
Key Action
Escape Closes the dialog.
Tab Cycles inside the dialog, since the rest of the page is inert.

Browser support

  • Used without guards: dialog, ::backdrop, @starting-style, transition-behavior: allow-discrete
  • Behind @supports: nothing

JavaScript

Optional enhancement: components/dialog/dialog.js. The component works without it.

Available since 7.0.0.