Dialog
A modal on the level surface: focus trap, inert page, Esc and focus return.
import { Dialog, DialogTrigger, DialogClose, … } from "@arcadia/ui"Actions go right-aligned with the primary last and Cancel as a ghost button. A dialog sits on --bg-elevated with the float edge as its border, 16px continuous corners and the dialog shadow. It is set down: a 200ms fade while it rises 8px into place on the critically damped --ease-settle over 320ms, with no overshoot and no zoom. The backdrop blurs the page by 8px under a light scrim, so the dialog stands out by depth rather than by darkness.
Dialog, Confirm Dialog or Tray? A Dialog is for a task with its own controls, such as renaming or settings. A Confirm Dialog only stops something that can't be undone. A question the product asks during work belongs in a Tray docked to the composer, where it doesn't block the page.
DialogHeader, DialogTitle, DialogDescription and DialogBody have no props of their own: they take the props of the element they render (<div>, <h2>, <p> and <div>).
Examples
rootStyle on DialogContent takes where the dialog sits and its width, a token of its kind.
| Knob | Takes | Default | Sets |
|---|---|---|---|
TokenRef<Measure> | 400px | Width (320 / 400 / 480 / 560 / 720 by size), capped by the viewport less a 16px gutter. |
Modal by default: focus is trapped, the page is inert and scroll-locked, and focus returns to the trigger on close. The title names the dialog and the description describes it, so always render a DialogTitle. The close button in the corner is labelled "Close" (closeLabel changes it).
- Esc
- Closes the dialog
- Tab
- Cycles focus inside the dialog
Dialog
modal- boolean | "trap-focus"Defaulttrue
true: focus trapped, the page behind inert, its scroll locked.false: none of that."trap-focus": focus trapped, the page left as it is.disablePointerDismissal- booleanDefaultfalse
- A press outside (on the backdrop) doesn't close it.
open- boolean
- Controlled open state.
defaultOpen- booleanDefaultfalse
- Initial open state when uncontrolled.
onOpenChange- (open: boolean, details: OpenChangeDetails) => void
- Called when the popup opens or closes, with why (
details.reason) and the event. onOpenChangeComplete- (open: boolean) => void
- Called once the open or close transition has finished.
render- RenderProp<{ disabled: boolean }>
- Replace the rendered element, e.g.
render={<a href="/settings" />}, or a function of props and state. disabled- boolean
- Native disabled: no pointer events, no tooltip, skipped by Tab.
focusableWhenDisabled- boolean
- Keep a disabled control focusable and hoverable (
aria-disabled), so a tooltip can say why. nativeButton- boolean
- Button semantics, inferred from what renders: a
<button>is native, an<a>stays a link, anything else getsrole="button"and key handling.falseforces button semantics on whatever renders (a link acting as a button).
Rest props go to <button> props.
rootStyle- RootStyle<DialogKnobs>
- Typed StyleX override for the dialog: layout and its
--dialog-*knobs. size- "sm" | "md" | "lg" | "xl" | "2xl"Default"md"
- Width 320 / 400 / 480 / 560 / 720, capped by the viewport minus 32px.
showClose- booleanDefaulttrue
- Top-right close button.
closeLabel- stringDefault"Close"
- Accessible name of the close button.
initialFocus- FocusTarget
- Where focus goes on open: an element,
falseto stay, or a function of how it opened. Default: the first field or button inside. finalFocus- FocusTarget
- Where focus goes on close. Default: wherever it was before it opened.
className- string
- A product's hook class for its own rules (being retired: new code sets
rootStyle).
Rest props go to <div> props.
divided- boolean
- Hairline above the actions (long bodies).
Rest props go to <div> props.
render- RenderProp<{ disabled: boolean }>
- Replace the rendered element, e.g.
render={<a href="/settings" />}, or a function of props and state. disabled- boolean
- Native disabled: no pointer events, no tooltip, skipped by Tab.
focusableWhenDisabled- boolean
- Keep a disabled control focusable and hoverable (
aria-disabled), so a tooltip can say why. nativeButton- boolean
- Button semantics, inferred from what renders: a
<button>is native, an<a>stays a link, anything else getsrole="button"and key handling.falseforces button semantics on whatever renders (a link acting as a button).
Rest props go to <button> props.