Alert Dialog
Ask someone to confirm an action before you continue.
Use an alert dialog when someone needs to answer a question before an action continues, such as deleting a project.
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
</script>
<AlertDialog>
<AlertDialogTrigger>Delete Project</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Delete this project?</AlertDialogTitle>
<AlertDialogDescription>
This permanently deletes the project, its files and its deployment
history for everyone on your team.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Delete</AlertDialogClose
>
</div>
</AlertDialogContent>
</AlertDialog>Installation
npm install @ui-sdk/svelte @ui-sdk/coreComposition
Use the following composition to build an AlertDialog:
AlertDialog
├── AlertDialogTrigger
├── AlertDialogBackdrop
└── AlertDialogContent
├── AlertDialogTitle
├── AlertDialogDescription
├── AlertDialogClose
└── AlertDialogClosePut the question in AlertDialogTitle, explain what happens in AlertDialogDescription, and use AlertDialogClose for each answer. Add AlertDialogBackdrop to dim the page.
A Close part reports its value when it closes. value="confirm" is the agreeing answer that the confirm helper recognizes.
Usage
Import the parts and put them together:
<script lang="ts">
import {
AlertDialog,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
</script>
<AlertDialog>
<AlertDialogTrigger>Delete Project</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogTitle>Delete this project?</AlertDialogTitle>
<AlertDialogDescription
>The project and its files will be deleted for everyone.</AlertDialogDescription
>
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose value="confirm">Delete</AlertDialogClose>
</AlertDialogContent>
</AlertDialog>Build Your Own
@ui-sdk/svelte/alert-dialog comes with a ready-made Alert Dialog. If you'd like to build your own, with your own names, start from the alertDialog primitive in @ui-sdk/core:
import { alertDialog } from "@ui-sdk/core/alert-dialog"
import { createParts } from "@ui-sdk/svelte/primitive"
const {
Root: Confirm,
Trigger: ConfirmTrigger,
Backdrop: ConfirmBackdrop,
Content: ConfirmContent,
Title: ConfirmTitle,
Description: ConfirmDescription,
Close: ConfirmClose,
} = createParts(alertDialog, {
trigger: "button",
backdrop: "div",
content: "div",
title: "h2",
description: "p",
close: "button",
})
export {
Confirm,
ConfirmBackdrop,
ConfirmClose,
ConfirmContent,
ConfirmDescription,
ConfirmTitle,
ConfirmTrigger,
}<Confirm>
<ConfirmTrigger>Delete Project</ConfirmTrigger>
<ConfirmBackdrop />
<ConfirmContent>
<ConfirmTitle>Delete this project?</ConfirmTitle>
<ConfirmDescription
>This permanently deletes the project.</ConfirmDescription
>
<ConfirmClose>Cancel</ConfirmClose>
<ConfirmClose value="confirm">Delete</ConfirmClose>
</ConfirmContent>
</Confirm>Each part keeps the behavior, state and data attributes of its ready-made counterpart: the content opens in the top layer, traps focus and makes the page inert. See Rendering and Custom Parts for more.
Controlled
Use bind:open when your app needs to read or control whether the dialog is open. State and Events explains controlled state and canceling a change.
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
let open = $state(false)
</script>
<div data-slot="stack">
<AlertDialog bind:open>
<AlertDialogTrigger>Review Changes</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Discard unsaved changes?</AlertDialogTitle>
<AlertDialogDescription
>Your edits will be lost if you leave without saving.</AlertDialogDescription
>
</div>
<div data-slot="row">
<AlertDialogClose>Keep Editing</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Discard</AlertDialogClose
>
</div>
</AlertDialogContent>
</AlertDialog>
</div>Shortcut
Give the trigger a shortcut to open the dialog from the keyboard, and put AlertDialogShortcut inside it to show the keys. Dialog covers the shortcut syntax.
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogShortcut,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
</script>
<AlertDialog>
<AlertDialogTrigger shortcut="mod+shift+h">
Clear History
<AlertDialogShortcut />
</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Clear your history?</AlertDialogTitle>
<AlertDialogDescription>
Recent searches and visited pages are removed from this device.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Clear</AlertDialogClose
>
</div>
</AlertDialogContent>
</AlertDialog>Animation
The content has data-starting-style while it opens and data-ending-style while it closes. Transition from those states, and the dialog stays mounted until the exit finishes:
[data-slot="alert-dialog-content"] {
transition:
opacity 0.2s,
translate 0.25s cubic-bezier(0.22, 1, 0.36, 1),
scale 0.25s cubic-bezier(0.22, 1, 0.36, 1);
&:is([data-starting-style], [data-ending-style]) {
opacity: 0;
translate: 0 16px;
scale: 0.96;
}
&[data-ending-style] {
transition-duration: 0.15s;
}
}<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
</script>
<AlertDialog>
<AlertDialogTrigger>Leave Workspace</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Leave this workspace?</AlertDialogTitle>
<AlertDialogDescription>
You'll lose access to its projects until someone invites you again.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Leave</AlertDialogClose
>
</div>
</AlertDialogContent>
</AlertDialog>Nested
An alert dialog opened inside another joins its stack, such as a second confirmation before something can't be undone. Escape closes the topmost one and returns focus to its trigger. The dialog underneath gets data-nested-open, so you can show which layer is active. To close both once the last question is answered, control the outer dialog with bind:open and close it when the inner one's onOpenChange reports returnValue "confirm".
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
let open = $state(false)
</script>
<AlertDialog bind:open>
<AlertDialogTrigger>Delete Workspace</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Delete this workspace?</AlertDialogTitle>
<AlertDialogDescription>
All 12 projects and their deployments will be deleted for everyone on
your team.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialog
onOpenChange={(nested, details) => {
if (!nested && details.returnValue === "confirm") {
open = false
}
}}
>
<AlertDialogTrigger data-variant="solid">Delete</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription
>There's no way to get the workspace back.</AlertDialogDescription
>
</div>
<div data-slot="row">
<AlertDialogClose>Go Back</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Delete Workspace</AlertDialogClose
>
</div>
</AlertDialogContent>
</AlertDialog>
</div>
</AlertDialogContent>
</AlertDialog>Async Action
An action on a Close part runs before it closes the dialog. Return a Promise to keep the dialog open until the work finishes. A resolved Promise closes it with the reason action; a rejection keeps it open and puts the error in that part's state.
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
AlertDialogTrigger,
} from "@ui-sdk/svelte/alert-dialog"
let attempts = 0
async function deleteProject() {
await new Promise((resolve) => setTimeout(resolve, 1200))
if (attempts++ === 0) {
throw new Error("Couldn't delete the project.")
}
}
</script>
<div data-slot="stack">
<AlertDialog pendingAnnouncement="Deleting project">
<AlertDialogTrigger>Delete Project</AlertDialogTrigger>
<AlertDialogBackdrop />
<AlertDialogContent>
<div data-slot="stack">
<AlertDialogTitle>Delete project?</AlertDialogTitle>
<AlertDialogDescription>
This permanently deletes the project, its files and its deployment
history for everyone on your team.
</AlertDialogDescription>
</div>
<AlertDialogClose
data-variant="solid"
value="confirm"
action={deleteProject}
>
{#snippet render({ props, state })}
<div data-slot="stack">
<p data-slot="hint" role="alert">
{state.error instanceof Error ? state.error.message : ""}
</p>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<button {...props}
>{state.pending
? "Deleting…"
: state.error
? "Try Again"
: "Delete"}</button
>
</div>
</div>
{/snippet}
</AlertDialogClose>
</AlertDialogContent>
</AlertDialog>
</div>While the action runs, its button stays focusable and gets data-pending, aria-busy, and aria-disabled. Other Close parts and dismissals cannot close the dialog until it settles. onDismissAttempt reports refused dismissals; a controller can still close it.
The root's pendingAnnouncement sets what screen readers hear after a second of waiting. The error clears on the next attempt or when the dialog opens again. The action also receives { source } after its event argument; Changes After an Await explains when to use it.
Confirm From Anywhere
The shared confirm() asks a question without any dialog markup. Render OverlayHost from @ui-sdk/svelte/overlays once near the root of your app, and it shows each question as an alert dialog. confirm() resolves true when someone agrees. Confirm Options lists what you can pass.
<script lang="ts">
import { confirm } from "@ui-sdk/svelte/alert-dialog"
import { OverlayHost } from "@ui-sdk/svelte/overlays"
import { useLog } from "./log"
const log = useLog()
async function archive() {
const yes = await confirm({
title: "Archive this project?",
description: "It moves to Archived, and you can restore it anytime.",
confirmLabel: "Archive",
})
log(yes ? "Archived the project" : "Kept the project")
}
</script>
<button type="button" onclick={archive}>Archive Project</button>
<OverlayHost />Confirm
Use createConfirm<Payload>() to ask a question from an event handler and await the answer. Create it once, then pass its controller to your alert dialog. The content's children snippet reads the question's data from state.payload; type it with DialogContentState<{ name: string }> from @ui-sdk/svelte/dialog.
await confirm({ name }) resolves true when the Close part with value="confirm" closes the dialog. Cancel, Escape, and other answers resolve false. This lets several rows share one dialog:
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
createConfirm,
} from "@ui-sdk/svelte/alert-dialog"
import type { DialogContentState } from "@ui-sdk/svelte/dialog"
const confirm = createConfirm<{ name: string }>()
let projects = $state(["Acme", "Globex", "Initech"])
let reset: HTMLButtonElement
async function remove(name: string, button: HTMLButtonElement) {
if (!(await confirm({ name }))) {
return
}
const row = button.closest("li")
const next = (
row?.nextElementSibling ?? row?.previousElementSibling
)?.querySelector("button")
const target = next ?? reset
target?.focus()
projects = projects.filter((project) => project !== name)
}
</script>
<div data-slot="stack">
<div data-slot="controls">
<button
type="button"
bind:this={reset}
onclick={() => (projects = ["Acme", "Globex", "Initech"])}
>
Reset Projects
</button>
</div>
<ul data-slot="list" aria-label="Projects">
{#each projects as name (name)}
<li data-slot="row">
{name}
<button
type="button"
data-size="sm"
aria-label={`Delete ${name}`}
onclick={(event) => remove(name, event.currentTarget)}
>
Delete
</button>
</li>
{:else}
<li data-slot="row">All projects deleted.</li>
{/each}
</ul>
<AlertDialog controller={confirm.controller}>
<AlertDialogBackdrop />
<AlertDialogContent>
{#snippet children(state: DialogContentState<{ name: string }>)}
<div data-slot="stack">
<AlertDialogTitle>Delete {state.payload?.name}?</AlertDialogTitle>
<AlertDialogDescription>
The project and its files will be deleted for everyone on your team.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Delete</AlertDialogClose
>
</div>
{/snippet}
</AlertDialogContent>
</AlertDialog>
</div>When the action removes the button that opened the dialog, choose a useful place for focus to go. This example focuses a neighboring row, or Reset Projects when the list is empty.
Queued Questions
Calls to the same confirm function wait their turn. You can ask two questions at once; the second appears only after the first has an answer.
<script lang="ts">
import {
AlertDialog,
AlertDialogBackdrop,
AlertDialogClose,
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
createConfirm,
} from "@ui-sdk/svelte/alert-dialog"
import type { DialogContentState } from "@ui-sdk/svelte/dialog"
import { useLog } from "./log"
const confirm = createConfirm<{ name: string }>()
const log = useLog()
async function review() {
const acme = confirm({ name: "Acme" })
const globex = confirm({ name: "Globex" })
const [first, second] = await Promise.all([acme, globex])
log(`Acme: ${first ? "confirmed" : "canceled"}`)
log(`Globex: ${second ? "confirmed" : "canceled"}`)
}
</script>
<div data-slot="stack">
<button type="button" onclick={review}>Review Two Projects</button>
<AlertDialog controller={confirm.controller}>
<AlertDialogBackdrop />
<AlertDialogContent>
{#snippet children(state: DialogContentState<{ name: string }>)}
<div data-slot="stack">
<AlertDialogTitle>Archive {state.payload?.name}?</AlertDialogTitle>
<AlertDialogDescription>
Archived projects leave the active list. You can restore them later.
</AlertDialogDescription>
</div>
<div data-slot="row">
<AlertDialogClose>Cancel</AlertDialogClose>
<AlertDialogClose data-variant="solid" value="confirm"
>Archive</AlertDialogClose
>
</div>
{/snippet}
</AlertDialogContent>
</AlertDialog>
</div>Styling
Each part has a data-slot, such as alert-dialog-content, and shows its state through the attributes listed under Data Attributes. Styling and Animation covers state attributes and entry and exit transitions.
AlertDialogContent
| Variable | What it holds |
|---|---|
--dialog-stack | How many dialogs are open above this one, nested inside it. 0 for the topmost. |
--keyboard-inset | How many pixels of the page the on-screen keyboard covers, 0 without one. |
--visual-viewport-width | The width of the part of the page you can see. |
--visual-viewport-height | The height of the part of the page you can see, above the keyboard. |
--visual-viewport-top | How far the part of the page you can see is scrolled from the top of the layout, as when the keyboard pushes it up. |
Data Attributes
AlertDialogTrigger
[data-slot="alert-dialog-trigger"]
| Attribute | What it means |
|---|---|
data-closed | Present while the dialog is closed, including while it animates out. |
data-disabled | Present when the trigger is disabled. |
data-open | Present while the dialog is open. On a trigger, while it's open from that trigger. |
AlertDialogBackdrop
[data-slot="alert-dialog-backdrop"]
| Attribute | What it means |
|---|---|
data-closed | Present while the dialog is closed, including while it animates out. |
data-open | Present while the dialog is open. On a trigger, while it's open from that trigger. |
data-starting-style | Present for the first frame. Style the state you animate from. |
data-ending-style | Present while the dialog animates out. Style the state you animate to. |
AlertDialogContent
[data-slot="alert-dialog-content"]
| Attribute | What it means |
|---|---|
data-closed | Present while the dialog is closed, including while it animates out. |
data-open | Present while the dialog is open. On a trigger, while it's open from that trigger. |
data-starting-style | Present for the first frame. Style the state you animate from. |
data-ending-style | Present while the dialog animates out. Style the state you animate to. |
data-keyboard | Present while the on-screen keyboard covers part of the page. |
AlertDialogClose
[data-slot="alert-dialog-close"]
| Attribute | What it means |
|---|---|
data-pending | Present on a Close part while its action runs. |
API Reference
Alert Dialog shares Dialog's options, except modal, closeOnOutsidePress, and presentation. It is always modal, never closes on an outside press, and does not present as a sheet.
AlertDialog
container?DocumentFragment | Element | null- Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
open?boolean- Whether the dialog is open. Pass it to control the dialog.
defaultOpen?boolean- Whether the dialog starts open, when uncontrolled. Focus moves into it once it's on the page.
onOpenChangeComplete?((open: boolean) => void)- Runs once the dialog has finished opening or closing, after any animation.
dismissible?boolean- Whether Escape, an outside press and Android's back gesture can close the dialog. Close parts, triggers and the controller always can. Pass
falsewhile a form has unsaved changes, and ask inonDismissAttempt. onDismissAttempt?((details: DialogDismissAttemptDetails) => void)- Runs when a dismissal was refused, because
dismissibleis false, an outside press doesn't close it, or a Close part's action is running. Ask "Discard changes?" here. persistentElements?(string | Element)[] | (() => Element[])- Elements that stay usable while the dialog makes the page inert, like a chat launcher or a password manager's button. Elements, CSS selectors, or a function that returns elements, read when the dialog opens. Presses on them never close it.
pendingAnnouncement?string- What screen readers hear once a Close part's action has been running for a second, so a slow action doesn't pass in silence. Set it in the page's language. An empty string says nothing.
debug?boolean- Logs every state change with its reason to the console, in development builds.
onOpenChange?((open: boolean, details: DialogOpenChangeDetails<AlertDialogOpenChangeReason>) => void)- Runs when the alert dialog wants to open or close.
details.reasonsays why,details.cancel()stops it, anddetails.returnValueis thevalueof the Close part that closed it. onPayloadChange?((payload: unknown, details: ChangeDetails<AlertDialogPayloadChangeReason>) => void)- Runs when a trigger or a controller with another payload opens the alert dialog, before
onOpenChange, or a controller'sopen({ payload })gives the open one another payload.details.cancel()keeps the previous payload. controller?PopupController<any, AlertDialogOpenChangeReason, {}, any, DialogPayloadChangeReason>- Opens, closes and inspects the alert dialog from anywhere. Create one with
createAlertDialogController()orcreateConfirm().
AlertDialogTrigger
disabled?boolean- Stops the trigger from opening the dialog. A native button can't be focused or pressed, unless
focusableWhenDisabledis set. focusableWhenDisabled?boolean- Keeps a disabled trigger in the tab order with
aria-disabledinstead of the native attribute, so it can host a tooltip that says why. Presses still do nothing. payload?unknown- Data for the content to render when this trigger opens the dialog, like the row it belongs to.
shortcut?string- A keyboard shortcut that presses the trigger, like
"mod+k".modis Command on Apple platforms and Control elsewhere. Setsaria-keyshortcuts, and a Shortcut part inside shows it. A modal dialog pauses the page's shortcuts while it's open. controller?PopupController<any, AlertDialogOpenChangeReason, {}, any, DialogPayloadChangeReason>- Connects a trigger outside the alert dialog to the alert dialog with this controller.
AlertDialogBackdrop
Dims the page behind the dialog. Renders a div and accepts its attributes and a render snippet.
AlertDialogContent
container?DocumentFragment | Element | null- Renders the popup into this element instead of in place, like a container inside a shadow root or an iframe. On web components, a CSS selector. Nested popups stay inside their parent.
initialFocus?DialogInitialFocus- Where focus goes when the dialog opens. Opening with touch focuses the dialog itself, so no on-screen keyboard appears.
finalFocus?PopupFocusTarget- Where focus goes when the dialog closes while focus is inside it.
keepMounted?boolean- Whether the dialog stays in the page, hidden, while closed. For animation libraries that drive the exit from state.
AlertDialogTitle
The question. Renders an h2, and the content uses it as its accessible name.
AlertDialogDescription
What happens if you agree. Renders a p, and the content uses it as its accessible description.
AlertDialogClose
value?string- Reported as
returnValueinonOpenChangeand by the controller'sopen().createConfirm()resolvestruefor"confirm". action?((event: Event | undefined, details: ActionDetails) => unknown)- Runs when the part is pressed, before the dialog closes. A returned Promise keeps the dialog open while it runs: the part gets
data-pending,aria-busyandaria-disabled, screen readers hear the dialog'spendingAnnouncementafter a second, and nothing but the controller closes the dialog. It closes with reason"action"once the Promise resolves. A rejection keeps it open and puts the reason in the part'serrorstate until the next press, or until the dialog opens again.details.sourcesays how the part was pressed, for changes made after anawait.
AlertDialogShortcut
shortcut?string- The keys to show. Inside a trigger, its
shortcut.
Confirm Options
The shared confirm() function uses these options when an OverlayHost renders the question. See Toasts and Confirms for the host setup. With your own dialog, createConfirm<Payload>() accepts the payload you choose instead.
titlestring- The question, like "Delete this project?".
description?string- What agreeing does.
confirmLabel?string- The agreeing button's text.
cancelLabel?string- The other button's text.
State
AlertDialogContent:
openboolean- Whether the dialog is open. False while it animates out.
modalboolean | "trap-focus"- The
modalsetting. Always true for an alert dialog. nestedboolean- Whether the dialog was opened from inside another dialog.
nestedOpenboolean- Whether a dialog opened from inside this one is open.
stacknumber- How many dialogs are open above this one, nested inside it.
payloadPayload | undefined- The payload of the last open, from the trigger that opened it or a controller, typed as the controller's
Payload. pendingboolean- Whether a Close part's action is running.
presentation"dialog"- Always
"dialog": an alert dialog never presents as a sheet.
AlertDialogClose:
pendingboolean- Whether this part's action is running.
errorunknown- Why this part's last action failed, until it's pressed again or the dialog opens again.
AlertDialogShortcut:
textstring- The shortcut formatted for this platform:
⌘Kon Apple platforms andCtrl+Kelsewhere. Empty without one.
Reasons
trigger-pressMouseEvent | KeyboardEvent | TouchEvent- Click, tap, Enter or Space on the part that opens, closes or toggles. Arrow keys that open a menu or submenu count too. A tap on a combobox's input that opens a sheet reports its
touchend. close-pressMouseEvent | KeyboardEvent | SubmitEvent- A Close part was pressed, or a form with
method="dialog"was submitted. escape-keyKeyboardEvent- Escape, or a submenu's close key (ArrowLeft in left-to-right layouts).
parent-closenone- The parent layer closed.
backEvent- Going back: the Android back button or gesture, or Backspace in an empty command input, which returns to the previous page.
actionMouseEvent | KeyboardEvent- A Close part's
actionfinished. shortcutKeyboardEvent- A keyboard shortcut, like a trigger's
shortcut. imperativenone- A controller or
apicall, or a property set on a web component.
onPayloadChange(payload, details) runs before onOpenChange when the dialog opens with another payload. Calling controller.open({ payload }) on an open dialog updates the payload with the reason imperative.
trigger-pressMouseEvent | KeyboardEvent | TouchEvent- Click, tap, Enter or Space on the part that opens, closes or toggles. Arrow keys that open a menu or submenu count too. A tap on a combobox's input that opens a sheet reports its
touchend. shortcutKeyboardEvent- A keyboard shortcut, like a trigger's
shortcut. imperativenone- A controller or
apicall, or a property set on a web component.
Controller
Use createAlertDialogController<Payload, Result>() for direct control. createConfirm<Payload>() returns a confirm function with its own controller.
isOpen()() => boolean- Whether it's open. False as soon as it starts closing, while it still animates out.
setOpen(open, options?)(open: boolean, options?: PopupChangeOptions<AlertDialogOpenChangeReason> | undefined) => boolean- Opens or closes it. Returns false when the change was canceled or it was already open or closed.
open(options?)(options?: (PopupChangeOptions<DialogPayloadChangeReason> & { payload?: Payload | undefined; anchor?: Anchor | undefined; signal?: AbortSignal | undefined; }) | undefined) => Promise<...>- Opens it, and resolves once it closes, with why, how and any return value. Resolves exactly once and never rejects: on the next close from any source, when
signalaborts, or with reason"unmount"when the component leaves the page. close(options?)(options?: (PopupChangeOptions<AlertDialogOpenChangeReason> & { returnValue?: Result | undefined; }) | undefined) => boolean- Closes it.
returnValueis passed to the promiseopen()returned. Returns false when the close was canceled or it was already closed. toggle(options?)(options?: PopupChangeOptions<AlertDialogOpenChangeReason> | undefined) => boolean- Opens it when it's closed, and closes it when it's open. Returns false when the change was canceled.
getPayload()() => Payload | undefined- The payload of the last open, from a controller or the trigger that opened it.
focus()() => void- Moves focus to the trigger that opened it last, or the first trigger.
Accessibility
Give the dialog a title and a description, and put the least destructive answer first. It follows the WAI-ARIA alert dialog pattern.
The dialog uses the alertdialog role and keeps the rest of the page inert while it's open. Screen readers announce its title and description. Opening it focuses the first Close part, so put Cancel first. A touch open focuses the content instead, so no on-screen keyboard appears. Escape closes the dialog, but pressing outside never does. Dialog covers other initial and final focus targets.
These behaviors are covered by automated tests:
- Opens with focus on the least destructive action.
- Is described by its message.
- An outside press keeps it open.
- Escape closes it and returns focus to the trigger.
- Tab cycles between its actions.
- The action reports and closes.
Runs in React, Vue and web components.