Skip to content
HyperDesign System

Dialog

A modal panel over a dimmed scrim. It traps focus, closes on Esc or a click outside, and hands focus back to the trigger. Built on Radix Dialog. Source: src/components/ui/dialog.tsx.

Default

A short form in a dialog. Submit closes it and the value updates behind it; Cancel, Esc and the X all close without saving.

name: Research agent

Confirm a destructive action

There is no AlertDialog primitive yet, so a confirm is composed from Dialog. Drop the X, put the safe choice first and give it focus, and name the action on the destructive button.

For a read-only notice, swap the corner X for the footer's built-in Close pill: showCloseButton={false} on the content, showCloseButton on the footer.

Long content

Known gap: DialogContent caps its height at --dialog-safe-max-h but has no overflow of its own, so long content spills past the rounded panel. Until it does, give the middle row min-h-0 and overflow-y-auto; header and footer stay pinned and the body scrolls between hairlines.

Motion and layering

Enter and exit are CSS keyframes (tw-animate), so a dialog closed mid-entrance finishes its exit from wherever it is rather than reversing smoothly. Under reduced motion the global rule cuts both to near-zero.

Dialog motion and layering tokens
ValueTokenUsage
50Overlay and panel. The brand ladder (--z-modal 200) is defined but not wired in yet.
scrimThe scrim: a dim wash with a light blur behind the panel.
200ms200ms enter: scrim fades in, panel fades and scales from 95%.
90ms90ms exit: both leave faster than they came.
expo-outThe panel's curve: quick start, long settle.
0.95Scale from --scale-enter (0.95), never from 0.
max-hHeight cap so the panel clears the viewport edges and the keyboard on a phone.

Props

Dialog parts and props
Part / propTypeDefaultDescription
DialogRadix Dialog.Root—open, defaultOpen, onOpenChange, modal. State lives here.
DialogTriggerRadix Dialog.Trigger—Use asChild around a Button. Gets aria-expanded and focus back on close.
DialogContentRadix Dialog.Content—Portals itself and renders its own overlay. Takes onEscapeKeyDown, onPointerDownOutside, onOpenAutoFocus. Width via className (sm:max-w-md).
showCloseButtonbooleantrueThe 32px X in the top-right corner, with a sr-only “Close” label.
DialogHeaderComponentProps<"div">—Title and description stack. Centred on a phone, left from sm.
DialogTitleRadix Dialog.Title—Required for the accessible name. 24px heading face.
DialogDescriptionRadix Dialog.Description—Wired to aria-describedby. Omit it and pass aria-describedby={undefined} on the content.
DialogFooterComponentProps<"div">—Actions, right-aligned from sm; stacked with the primary on top on a phone.
showCloseButtonbooleanfalseAppends a secondary “Close” pill after the children.
DialogCloseRadix Dialog.Close—Any element that closes the dialog. Use asChild around a Button.
DialogPortal, DialogOverlayRadix—Exported for custom shells. DialogContent already includes both.

Best practices

When to use

  • A dialog interrupts. Use it for a decision or a short form that must finish before the page continues.
  • Long or browsable content belongs in a Sheet or its own page, not a tall dialog.
  • Destructive actions confirm here, with the safe choice focused first. There is no AlertDialog yet.

Behavior

  • Esc, a scrim click and the X all close. Use onPointerDownOutside to block scrim dismissal only when input would be lost.
  • Focus moves into the panel on open and back to the trigger on close; don't fight either.
  • Wrap fields in a form so Enter submits; close in the submit handler once the save succeeds.

Accessibility

  • Every dialog needs a DialogTitle; hide it with sr-only if the design has no visible heading.
  • DialogDescription becomes aria-describedby. Keep it to one or two sentences.
  • The corner X has a sr-only “Close” label; a custom close button needs one too.

Content

  • Title as a question for a confirm (“Delete this thread?”), as a verb phrase for a task (“Rename agent”).
  • Button labels say what happens: Delete, Save, Keep thread. Not OK and Cancel.