Usage
Dialog overlays the page with a single focused task: confirm a serious action, collect required fields, or surface information the operator must not miss. It demands attention and temporarily suspends the background. The mental model is "stop, decide, continue"—and the component earns its weight only when the alternative is silent data loss or a mis-sent decision. Overuse trains users to dismiss without reading, so a dispatch console should have few modals, and each one should be consequential.
TakeoverModal or a dedicated page.
If a message must persist until the operator explicitly acknowledges it (license warnings, on-call handoffs, training debriefs), a modal is the right weight. A dismissible Snackbar or inline Banner lets the user miss it; a modal guarantees a click.
Popover or inline expansion. Forcing them into a modal interrupts the flow and trains operators to dismiss modals reflexively.
When users need to keep a data table, incident, or transcript visible while they work, use Drawer (anchored side panel) or TakeoverModal (full-screen workflow). A modal hides the context behind a backdrop; long flows feel airless inside one.
Background-sync toasts, save-succeeded confirmations, and other disposable outcomes belong in Snackbar. Modals should not announce routine events.
If the content could live inline (policy notes, read-only detail), a modal adds weight without adding clarity. Ask whether the user needs a blocking moment or just information—if it's the latter, a Banner or static section is better.
| Shape | Purpose | Emphasis |
|---|---|---|
| Standard confirm / form | Single decision or short form | One primary action, explicit cancel |
| Destructive primary | Irreversible operations | Destructive type on the primary button; body lists impacts |
| Non-dismissible | Required user choice | Rare; veto Escape with details.cancel() only with product approval. A backdrop press never closes a Dialog |
| Stacked | Secondary confirmation on top of the first | Keep depth shallow; each layer must add clarity, not noise |
DialogContent renders the header: the title (an h2) and an optional description. There is no close button in the header; compose DialogClose in the footer. DialogBody holds the task. DialogFooter holds the actions, aligned to the inline end; put the primary action last. When an action is destructive, the primary button uses the destructive type so its styling matches its consequence.
Specific, not generic. "Remove responder from incident?" rather than "Are you sure?" The title is the accessible name for the dialog; it should read sensibly without the body.
Short, scannable. Use bullets for lists of impacts and keep the body under one visual beat. If the consequence lives in paragraph five of a wall of copy, nobody will read it—pull it up.
DialogClose, because the dialog has no close icon. Match the button type to the action: destructive primaries for destructive verbs, primary for commits, secondary for de-emphasized alternates.
formatMessage; test the longest expected translation inside the modal's width before shipping.close-request to onOpenChange; call details.cancel() on a cancelable request to keep the dialog open, for example while a form has unsaved changes. A forced close, such as a repeated Escape the browser does not let the page block, is not cancelable and is still reported once.dialog or alertdialog) and name; the dialog takes no id prop.@prepared911/ui/styles.css, it sets data-theme on the document element, and it renders DesignSystemEnvironment for the locale and time zone (see remote composition). A dialog needs no provider, portal root, or z-index. DialogContent is a native <dialog> opened with showModal(), so it paints in the top layer above the shell's chrome, a slot's overflow, transform, or z-index cannot clip or cover it, and the page behind it does not scroll. It renders in place, so it takes the theme of the nearest data-theme region around its trigger.
DialogTrigger clones its child, so a RemoteComponent mount cannot be the trigger. A remote uses a dialog in one of two ways:
@prepared911/ui/dialog. It imports no @prepared911/ui stylesheet; the shell's covers it.RemoteComponent fills the content slot: