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 confirmText
string
Dialog open, defaultOpen, onOpenChange
Base UI default false
DialogContent size
'sm' | 'default' | 'lg' default 'default'
DialogContent showCloseButton
boolean default role === dialog
DialogContent initialFocus, finalFocus
Base UI default from the footer, the trigger
DialogFooter defaultAction
'primary' | 'dismiss' | 'none' default auto
DialogFooter meta
ReactNode
DialogAction onAction
() => void | Promise
DialogAction close
boolean default true with onAction
DialogAction variant, size
ButtonProps default 'primary', 'sm'
DialogConfirmField label
(name) => ReactNode default "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)

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.