GitHub

Dialog

A modal dialog compound that wraps Radix Dialog with the full anatomy — Trigger, Content, Header, Body, Footer, Title, Description and Close. Use it when an action must block the flow and demand a decision. It traps focus, restores focus to the trigger on close, and is dismissible via the close button, backdrop, or Escape — each opt-outable.

beta
tsx
import { Dialog } from "@idinstudio/ui";

Preview

Real, interactive examples rendered from the live component.

tsx
<Dialog>
  <Dialog.Trigger asChild><Button severity="danger">Delete</Button></Dialog.Trigger>
  <Dialog.Content size="md">
    <Dialog.Header>
      <Dialog.Title>Delete workspace?</Dialog.Title>
      <Dialog.Description>This cannot be undone.</Dialog.Description>
    </Dialog.Header>
    <Dialog.Footer>
      <Dialog.Close asChild><Button variant="ghost">Cancel</Button></Dialog.Close>
      <Button severity="danger">Delete</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog>

Props

Every styling and behavioural attribute is a typed prop — no className at the usage site.

PropTypeDefaultDescription
openbooleanControlled open state. Pair with onOpenChange.
defaultOpenbooleanUncontrolled initial open state.
onOpenChange(open: boolean) => voidFires whenever the open state changes.
modalbooleantrueWhen false the underlying page stays interactive (non-modal).
Content.size"sm" | "md" | "lg" | "full""md"Panel width preset. full renders an edge-to-edge sheet with inset.
Content.closeOnOutsideClickbooleantrueWhen false, clicking the backdrop does not close the dialog.
Content.closeOnEscapebooleantrueWhen false, Escape does not close the dialog.
Content.showCloseButtonbooleantrueWhen false, hides the top-right X close affordance.

Guidelines

When to reach for this component, and when not to.

Do

  • Use Dialog when a decision must block the flow — destructive confirms, required input.

  • Write the title as a question with a specific noun — "Delete workspace?".

  • State the consequence and permanence in the description before a destructive action.

  • Keep the primary action in the Footer and label it with the exact verb-object.

Don't

  • Don't use a Dialog for transient acknowledgements — use a Toast.

  • Don't write vague titles like "Are you sure?" or "Confirm action".

  • Don't disable every dismissal (Escape, backdrop, X) without an explicit Footer choice.

  • Don't nest dialogs or trigger one from inside another.


Accessibility

The component's a11y contract. WCAG 2.1 AA is the non-negotiable baseline.

Keyboard

KeyAction
TabMove focus forward within the dialog (trapped).
Shift + TabMove focus backward within the dialog.
EscClose the dialog (unless closeOnEscape={false}).
Enter / SpaceActivate the focused button.

ARIA & screen reader

Contract
  • Content has role="dialog" and aria-modal="true" when modal (the default).
  • Dialog.Title and Dialog.Description wire to the content via aria-labelledby / aria-describedby.
  • On open, focus moves to the first focusable element inside the content and is trapped.
  • On close, focus returns to the trigger element.
  • The title renders inside an <h2> (Radix); the close button is labelled aria-label="Close".
  • Announces "<Title>, dialog" on open.