Skip to main content

Theme config

Theme mode

Color palette

Grays

Border radii/radiuses/radiopedes/you know
Border radius
Field border radius
Button border radius
All
Components
Guides
API
Recent

    Components

    Dialog

    Full supportSupported since v135.Partial supportMissing: overlay.Partial supportMissing: dialog-closedby, overlay.

    Usage

    Non-modal

    • Toast: informative but non-interruptive

    No JavaScript required

    In browsers that support Invoker Commands you can toggle a <dialog> without JavaScript by using the commandfor and command attributes.

    Are you sure?

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus.

    <button
    commandfor="example-dialog-html"
    command="show-modal"
    class="ui-button ui-outlined"
    >
    Open dialog
    </button>
    <dialog
    id="example-dialog-html"
    class="ui-dialog ui-card ui-elevated"
    role="alertdialog"
    aria-labelledby="dialog-heading"
    aria-modal="true"
    >
    <hgroup>
    <h2 id="dialog-heading" class="ui-h4">Are you sure?</h2>
    </hgroup>
    <div class="ui-content">
    <p>
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales,
    nulla sit amet porttitor rhoncus. Lorem ipsum dolor sit amet, consectetur
    adipiscing elit. Vivamus sodales, nulla sit amet porttitor rhoncus.
    </p>
    </div>
    <div class="ui-actions">
    <button
    commandfor="example-dialog-html"
    command="close"
    class="ui-button"
    type="button"
    >
    Cancel
    </button>
    <button
    commandfor="example-dialog-html"
    command="close"
    class="ui-button ui-filled"
    type="button"
    >
    Save
    </button>
    </div>
    </dialog>

    How to close a dialog

    You can use it like this: <dialog closedby=""> and give it the following values:

    Attr valueDescription
    closedby="any"Click anywhere outside of the dialog to close it.
    closedby="closerequest"Device-specific way to close, ex: Esc on desktop, back button on mobile, and whatever dismiss action assistive tools use.
    closedby="none"You have to handroll a closing solution yourself.

    How to close

    Choose a closing behavior:
    <button
    commandfor="closing-behaviors-dialog-html"
    command="show-modal"
    class="ui-button ui-outlined"
    >
    Open dialog
    </button>
    <dialog
    id="closing-behaviors-dialog-html"
    class="ui-dialog ui-card ui-elevated"
    closedby="any"
    >
    <hgroup>
    <h2 class="ui-h4">How to close</h2>
    </hgroup>
    <div class="ui-content">
    <fieldset class="ui-fieldset">
    <legend>Choose a closing behavior:</legend>
    <div class="ui-field-group" role="group">
    <label class="ui-radio">
    <input type="radio" name="closedby-demo" value="any" checked />
    <span class="ui-label">any</span>
    </label>
    <label class="ui-radio">
    <input type="radio" name="closedby-demo" value="closerequest" />
    <span class="ui-label">closerequest</span>
    </label>
    <label class="ui-radio">
    <input type="radio" name="closedby-demo" value="none" />
    <span class="ui-label">none</span>
    </label>
    </div>
    </fieldset>
    </div>
    <div class="ui-actions">
    <button
    commandfor="closing-behaviors-dialog-html"
    command="close"
    class="ui-button"
    >
    Close manually
    </button>
    </div>
    </dialog>
    <script>
    const radios = document.querySelectorAll('input[name="closedby-demo"]')
    radios.forEach((radio) => {
    radio.addEventListener("change", () => {
    const dialog = radio
    .closest(".example-preview")
    ?.querySelector("#closing-behaviors-dialog-html")
    if (dialog instanceof HTMLDialogElement) {
    dialog.setAttribute("closedby", radio.value)
    }
    })
    })
    </script>

    Accessibility

    • The tabindex attribute must not be used on the<dialog> element.

    Role & attributes

    Role/attributeUsage
    role="dialog"Identifies the element that serves as the dialog container.
    role="alertdialog"If the dialog is a confirmation window communicating an important message that requires a confirmation or other user response.
    aria-labelledby="IDREF"Gives the dialog an accessible name by referring to the element that provides the dialog title.
    aria-describedby="IDREF"Gives the dialog an accessible description by referring to the dialog content that describes the primary message or purpose of the dialog.
    aria-modal="true"Tells assistive technologies that the windows underneath the current dialog are not available for interaction (inert).

    Keyboard support

    KeyFunction
    Tab
    • Moves focus to next focusable element inside the dialog.
    • When focus is on the last focusable element in the dialog, moves focus to the first focusable element in the dialog.
    Shift + Tab
    • Moves focus to previous focusable element inside the dialog.
    • When focus is on the first focusable element in the dialog, moves focus to the last focusable element in the dialog.
    EscCloses the dialog.

    Source: w3.org, MDN

    API

    Dialog API

    TypeModifiersDefaultDescription
    Alignmentdefault, .ui-actions.ui-align-end-Alignment for the actions.
    Close behavior[closedby]-How the dialog can be closed. "any" also closes it on a click outside.

    Parts

    PartDescription
    dialog.ui-dialogContainer element.
    <hgroup>The dialog header.
    .ui-contentThe dialog content.
    .ui-actionsA group of actions, such as buttons.

    Add .ui-card and .ui-elevated to the root for card styles.

    Browser support

    Full supportSupported since v135.Partial supportMissing: overlay.Partial supportMissing: dialog-closedby, overlay.

    See also the full browser support guide.

    Installation

    Dependencies

    @layer components.extended {
    :where(.ui-dialog) {
    --_motion: var(--motion, 1);
    inline-size: 100%;
    inset: 0;
    margin: auto;
    margin-block-start: 15%;
    max-inline-size: calc(100% - var(--size-4));
    padding-block: 0;
    pointer-events: none;
    position: fixed;
    @media (width > 600px) {
    max-inline-size: 60ch;
    }
    /* Animation */
    /* There's no close animation, intentionally */
    opacity: 0;
    &::backdrop {
    backdrop-filter: blur(1px);
    background-color: rgba(0, 0, 0, 0.5);
    }
    &:not([open]) {
    display: none;
    }
    &[open] {
    pointer-events: all;
    }
    .ui-actions {
    justify-content: end;
    padding-inline: var(--size-3) var(--size-1);
    }
    &[open] {
    opacity: 1;
    transition:
    display calc(0.2s * var(--_motion)) allow-discrete,
    overlay calc(0.2s * var(--_motion)) allow-discrete,
    opacity calc(0.2s * var(--_motion)) var(--ease-out-1);
    @starting-style {
    opacity: 0;
    }
    }
    &[open] {
    margin-block-start: 15%;
    @starting-style {
    opacity: 0;
    }
    }
    }
    :where(html:has(.ui-dialog[open])) {
    block-size: 100%;
    overflow: hidden;
    }
    }
    @layer components.root {
    :where(.ui-card) {
    --_bg-tonal: var(--surface-tonal);
    --_bg-elevated: var(--surface-elevated);
    --_bg-surface: var(--surface-default);
    --_border-color: var(--border-color);
    --_card-bg-color: var(--_bg-surface);
    --_card-border-color: transparent;
    --_card-border-width: 0;
    --_card-shadow: none;
    --_shadow-light: var(--shadow-3);
    --_shadow-dark: var(--shadow-4);
    --_shadow-elevated: var(--_shadow-light);
    @container style(--color-scheme: dark) {
    --_shadow-elevated: var(--_shadow-dark);
    }
    background-color: var(--_card-bg-color);
    border-color: var(--_card-border-color);
    border-radius: var(--border-radius);
    border-style: solid;
    border-width: var(--_card-border-width);
    box-shadow: var(--_card-shadow);
    display: flex;
    flex-direction: column;
    gap: var(--size-3);
    overflow: hidden;
    padding-inline: 0;
    position: relative;
    /* Variants */
    &.ui-text {
    --_card-bg-color: transparent;
    --_card-border-color: transparent;
    --_card-border-width: 0;
    --_card-shadow: none;
    }
    &.ui-tonal {
    --_card-bg-color: var(--_bg-tonal);
    --_card-border-width: 1px;
    }
    &.ui-elevated {
    --_card-bg-color: var(--_bg-elevated);
    --_card-shadow: var(--_shadow-elevated);
    }
    &.ui-outlined {
    --_card-bg-color: var(--_bg-surface);
    --_card-border-color: var(--_border-color);
    --_card-border-width: 1px;
    }
    & > :where(hgroup, .ui-content) {
    padding-inline: var(--size-3);
    &:last-child {
    padding-block-end: var(--size-3);
    }
    }
    /* Header */
    & > hgroup {
    padding-block: var(--size-3) 0;
    /* Top paragraph */
    & > p:first-of-type:first-child {
    font-size: var(--font-size-0);
    line-height: 1.3;
    }
    :where(h1, h2, h3, h4, h5, h6):last-child {
    margin-block-end: 0;
    }
    /* Bottom paragraph */
    & > p:last-of-type:last-child:not(:first-child) {
    font-size: var(--font-size-1);
    }
    }
    /* Content */
    & > .ui-content:where(:only-child, :first-child) {
    padding-block: var(--size-3) var(--size-4);
    }
    /* Actions */
    & > .ui-actions {
    display: flex;
    gap: var(--size-2);
    margin-block: var(--size-2) 0;
    padding-block-end: var(--size-2);
    padding-inline: var(--size-3);
    &:has(.ui-button:first-child[class="ui-button"]) {
    padding-inline: var(--size-1) var(--size-3);
    }
    &:has(.ui-button:not([class="ui-button"])) {
    padding-block-end: var(--size-2);
    }
    /* Alignment */
    &.ui-align-end {
    justify-content: end;
    &:has(.ui-button:first-child[class="ui-button"]) {
    padding-inline: var(--size-3) var(--size-1);
    }
    }
    }
    }
    }