Dialog
Asks for a decision on top of the page.
Role
dialogdefault
alertdialog
alertdialog + confirmText
Sizes
sm
defaultdefault
lg
States
open
pending
failed
typed differs
typed matched
Usage
When to use it
- Use
- The person must finish or abandon a short task before the page makes sense again: publish a prompt, rotate a key.
- Use
- An action needs one yes or no before anything changes: stop a run, revert a release.
- Use
- The action cannot be undone and deletes something with a name: an agent, a workspace, a data source.
- Not for
- The task takes more than a screen of fields, or the page must stay in view beside it. Use Sheet.
- Not for
- A few lines next to a control, and the page stays usable. Use Popover.
- Not for
- The message reports a result and needs no answer, or the action can be undone. Use Toast.
Rules
9 rules
- Must
- End the footer with DialogClose (paper) and at most one DialogAction; make it variant="destructive" when the action cannot be undone. Because the footer is one group of actions, and a second ink button splits the eye; coral beside ink gives the group two leads.
- Must
- Pass open and onOpenChange; never render the dialog conditionally. Because a conditional dialog unmounts before its close fades and Base UI cannot return focus to the trigger.
- Must
- Return the request's promise from onAction; never close the dialog in onClick. Because closing in onClick drops a failed request without a word, where onAction holds the dialog pending and shows the error.
- Never
- Set defaultAction="primary" beside a destructive action; leave it unset or "dismiss". Because Enter outside a field would then press Delete.
- Must
- Use role="alertdialog" with confirmText and a DialogConfirmField to delete for good a thing that holds others, such as an agent or a workspace; one record takes a plain alertdialog. Because a stray click or Enter deletes it for good, and typing the name makes the person read which one; typing for every record trains them to type through the real one.
- Never
- Confirm an action that can be undone; do it and show a toast with Undo. Because a confirm on every undoable action trains people to click through, so they click through the real one too.
- Must
- Title an alert as a question that names the thing ("Stop run 4812?") and work as a verb phrase ("Rotate support-key"); never "Are you sure?". Because a screen reader opening the dialog hears the title first, and "Are you sure?" names neither the action nor the object.
- Must
- Give every dialog a DialogTitle, with className="sr-only" when the design shows none. Because Base UI names the dialog from DialogTitle, so without one a screen reader announces only "dialog".
- Never
- Add a shadow or a custom scrim to DialogContent; pass size or placement classes only. Because Glaze has no shadows, so the dialog lifts with its ring on the scrim, and a hand-made scrim stands apart from Sheet's.
State order
4 states, highest first
- 1
- pending
- 2
- failed
- 3
- typed differs
- 4
- open
Accessibility
4 checks
- The popup is role="dialog" or "alertdialog", named by DialogTitle and described by DialogDescription.
- Focus lands from the footer default or a data-autofocus element; Tab cycles inside; the page behind is inert.
- A failed action shows a role="alert" message above the footer, in coral's word colour with an icon.
- The typed-confirm field is described by its hint and announces only the match, not every keystroke.
Keyboard
3 keys
- Escape
- Closes this dialog only and returns focus to the trigger; nothing while an action runs.
- Enter
- Presses the footer's default action when focus is not on a control; never confirms a typed name.
- Tab
- Moves through the dialog's controls and wraps from the last to the first.
Edge cases
6 edge cases
- The deleted row is gone
- Pass finalFocus the next row's actions button, since the trigger left with the row.
- More rows than the screen holds
- The dialog stops 32px from the top and bottom and the body scrolls; past three screens use a Sheet.
- A 10-second action
- The action's arc turns; Cancel, the X, Escape and the scrim do nothing until it settles.
- Phone, 390 wide
- The dialog leaves 16px of page each side; the footer stays one row and its meta truncates first.
- A menu or a second dialog inside
- Escape closes the innermost; a parent dialog dims under its child.
- Reduced motion
- Open and close cut instead of fading; a running arc stays drawn.
API
12 props
Dialog role'dialog' | 'alertdialog'default'dialog'Dialog confirmTextstringDialog open, defaultOpen, onOpenChangeBase UIdefaultfalseDialogContent size'sm' | 'default' | 'lg'default'default'DialogContent showCloseButtonbooleandefaultrole === dialogDialogContent initialFocus, finalFocusBase UIdefaultfrom the footer, the triggerDialogFooter defaultAction'primary' | 'dismiss' | 'none'defaultautoDialogFooter metaReactNodeDialogAction onAction() => void | PromiseDialogAction closebooleandefaulttrue with onActionDialogAction variant, sizeButtonPropsdefault'primary', 'sm'DialogConfirmField label(name) => ReactNodedefault"Type <name> to confirm"
Tokens
27 tokens
--shell- #f1f1f0
--paper- #ffffff
--line- rgb(5 5 5 / 0.10)
--line-faint- rgb(5 5 5 / 0.06)
--scrim- rgb(5 5 5 / 0.24)
--radius-shell- 14px
--radius-panel- 11px
--shell-inset- 3px
--overlay-sm- 400px
--overlay- 480px
--overlay-lg- 640px
--space-4- 16px
--space-5- 20px
--space-8- 32px
--type-heading- 500 20px/1.3 var(--font-sans)
--type-body-sm- 400 13px/1.5 var(--font-sans)
--type-caption- 400 12px/1.35 var(--font-sans)
--text-1- rgb(5 5 5 / 0.80)
--text-2- rgb(5 5 5 / 0.60)
--fail-tint- #fbe9e5
--fail-text- #b23c29
--ok-text- #1a7640
--field- #efefef
--z-overlay- 50
--dur-overlay- 200ms
--dur-state- 160ms
--ease-out- cubic-bezier(0.2, 0.8, 0.2, 1)