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

A minimally styled window overlaid on the main content. By design the Dialog is minimal with zero content to allow for both modal and non-modal use.

Full support Supported since v135. Partial support Missing: container-style-queries, overlay. Partial support Missing: 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.
<button commandfor="example-dialog-html" command="show-modal" class="button outlined">
Open dialog
</button>
<dialog id="example-dialog-html" class="dialog card elevated" role="alertdialog" aria-labelledby="dialog-heading"
aria-modal="true">
<hgroup>
<h2 id="dialog-heading" class="h4">Are you sure?</h2>
</hgroup>
<div class="content">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus sodales,
nulla sit amet porttitor rhoncus.
</div>
<div class="actions">
<button commandfor="example-dialog-html" command="close" class="button" type="button">
Cancel
</button>
<button commandfor="example-dialog-html" command="close" class="button 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 value Description
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="button outlined">
Open dialog
</button>
<dialog id="closing-behaviors-dialog-html" class="dialog card elevated" closedby="any">
<hgroup>
<h2 class="h4">How to close</h2>
</hgroup>
<div class="content">
<div class="fieldset">
<p class="legend">Choose a closing behavior:</p>
<div class="field-group" role="group">
<label class="radio">
<input type="radio" name="closedby-demo-html" value="any" checked>
<span>any</span>
</label>
<label class="radio">
<input type="radio" name="closedby-demo-html" value="closerequest">
<span>closerequest</span>
</label>
<label class="radio">
<input type="radio" name="closedby-demo-html" value="none">
<span>none</span>
</label>
</div>
</div>
</div>
<div class="actions">
<button commandfor="closing-behaviors-dialog-html" command="close" class="button">
Close manually
</button>
</div>
</dialog>
<script>
const radios = document.querySelectorAll('input[name="closedby-demo-html"]')
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/attribute Usage
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

Key Function
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.
Esc Closes the dialog.

Source: w3.org, MDN

API

Type Modifiers Default Description
Styles .dialog.card.elevated Included The dialog uses card styles by default.
Closed by closedby attribute - The attribute used to control close behavior.

Browser support

Full support Supported since v135. Partial support Missing: container-style-queries, overlay. Partial support Missing: dialog-closedby, overlay.

See also the full browser support guide.

Installation

Dependencies

@layer components.extended {
:where(.dialog) {
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);
@media (prefers-reduced-motion: reduce) {
backdrop-filter: none;
}
}
&:not([open]) {
display: none;
}
&[open] {
pointer-events: all;
}
.actions {
justify-content: end;
padding-inline: var(--size-3) var(--size-1);
}
&[open] {
opacity: 1;
transition: display 0.2s allow-discrete,
overlay 0.2s allow-discrete,
opacity 0.2s var(--ease-out-1);
@starting-style {
opacity: 0;
}
}
@media (prefers-reduced-motion: no-preference) {
&[open] {
margin-block-start: 15%;
@starting-style {
opacity: 0;
}
}
}
}
:where(html:has(.dialog[open])) {
block-size: 100%;
overflow: hidden;
}
}
@layer components.root {
:where(.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 */
&.text {
--_card-bg-color: transparent;
--_card-border-color: transparent;
--_card-border-width: 0;
--_card-shadow: none;
}
&.tonal {
--_card-bg-color: var(--_bg-tonal);
--_card-border-width: 1px;
}
&.elevated {
--_card-bg-color: var(--_bg-elevated);
--_card-shadow: var(--_shadow-elevated);
}
&.outlined {
--_card-bg-color: var(--_bg-surface);
--_card-border-color: var(--_border-color);
--_card-border-width: 1px;
}
&> :where(hgroup, .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 {
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 */
&>.content:where(:only-child, :first-child) {
padding-block: var(--size-3) var(--size-4);
}
/* Actions */
&>.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(.button:first-child[class="button"]) {
padding-inline: var(--size-1) var(--size-3);
}
&:has(.button:not([class="button"])) {
padding-block-end: var(--size-2);
}
/* Alignment */
&.align-end {
justify-content: end;
&:has(.button:first-child[class="button"]) {
padding-inline: var(--size-3) var(--size-1);
}
}
}
}
}