Dialogs
A dialog interrupts to ask for a decision or to show information that needs an answer before
anything else can happen. It is a native popover in the top layer, so opening it is a matter of
pointing a trigger at it with popovertarget; the browser handles Escape and the light dismiss.
Keep dialogs for choices that really cannot wait. For a message that only confirms what happened, use a snackbar instead.
Usage
Reset settings?
This will reset your app preferences back to their default settings.
<script lang="ts">
let dialog: ReturnType<typeof Dialog> | undefined = $state()
</script>
<Button popovertarget="simple-dialog">Show Dialog</Button>
<Dialog
bind:this={dialog}
headline="Reset settings?"
supportingText="This will reset your app preferences back to their default settings."
id="simple-dialog"
divider
>
{#snippet icon()}
<Icon>settings</Icon>
{/snippet}
{#snippet actions()}
<Button
onclick={() => {
dialog?.hidePopover()
}}
variant="text">Cancel</Button
>
<Button
onclick={() => {
dialog?.hidePopover()
}}
variant="text">Accept</Button
>
{/snippet}
</Dialog>Methods
Bind a reference to the dialog with bind:this to call its methods imperatively. Type
the reference with ReturnType<typeof Dialog>; it is undefined until the component has mounted, so call through ?..
| Method | Type | Description |
|---|---|---|
showPopover | () => void | Shows the dialog. |
hidePopover | () => void | Hides the dialog. |
Scrollable content
Anything you pass as children goes into a scrolling area between the supporting text and the actions, so the headline and the buttons stay in place while long content scrolls. The dialog itself never grows past the viewport.
Terms of service
Paragraph 1. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 2. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 3. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 4. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 5. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 6. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 7. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 8. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 9. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 10. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 11. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
Paragraph 12. Consecutive paragraphs make the body taller than the dialog, which is what puts the scroll bar on the content instead of on the page.
<Dialog headline="Terms of service" id="terms" divider>
<p>...</p>
{#snippet actions()}
<Button variant="text" onclick={() => dialog?.hidePopover()}>Close</Button>
{/snippet}
</Dialog>Without animation
A dialog fades in over 250ms. Pass quick to skip that, for example when the dialog opens
as a direct answer to a keystroke and the delay would get in the way.
<Dialog
quick
headline="Rename file"
id="rename-dialog"
/>Accessibility
The surface renders role="dialog" with aria-modal="true". The headline labels it through aria-labelledby and the supportingText describes it
through aria-describedby, so both are announced when the dialog opens.
While the dialog is open every other element on the page is marked inert, which keeps
both the keyboard and the screen reader cursor inside it. Focus moves to the dialog on open and
back to whatever was focused before on close. Escape and a click on the scrim close the dialog,
so always offer a cancelling action as well; a dialog that must not be dismissed by accident should
not rely on the scrim alone.
Theming
The dialog has no tokens of its own. It draws on the surface and on-surface roles, uses secondary for the icon, on-surface-variant for the supporting text and scrim for the backdrop, so it follows the theme without further
configuration.
The width is a plain CSS property: the container is 37rem wide, at least 19.5rem, and never wider than the viewport. Override it with style or a class when a dialog needs to be narrower or wider.
<Dialog
headline="Rename file"
id="rename-dialog"
style="width:24rem"
/>API
Attributes
Everything else you pass is forwarded to the popover element, so id, class, style and event handlers such as ontoggle work as expected.
| Attribute | Type | Default | Description |
|---|---|---|---|
headline | string | Required. Title of the dialog, and its accessible name. | |
supportingText | string | undefined | undefined | Explanatory line below the headline. It becomes the dialog's description. |
icon | Snippet | undefined | undefined | Icon above the headline. Adding one also centers the headline. |
actions | Snippet | undefined | undefined | Buttons at the bottom of the dialog, aligned to the end. |
divider | boolean | undefined | undefined | Draws a divider under the header. Useful when the content scrolls. |
quick | boolean | false | Opens and closes without the fade transition. |
element | HTMLElement | undefined | undefined | Bindable reference to the popover element. |