Overlays

Dialog

A modal dialog.

Client component9 exports

Focus trapping, focus restoration, scroll locking and Escape handling come from Radix — behaviour that is genuinely difficult to get right and where hand-rolled versions reliably produce accessibility bugs. Everything visible is ABBA's.

Import

import {
  Dialog,
  DialogTrigger,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogBody,
  DialogFooter,
  DialogClose,
} from "@abbainitiative/ui";

Also available from the subpath entry @abbainitiative/ui/dialog.

This component carries its own "use client" directive. You can still render it from a Server Component — you simply cannot pass it a function prop, because functions do not serialise across the boundary.

Examples

Confirmation dialog

Open it, then press Tab repeatedly — focus never leaves. Escape closes and returns focus to the trigger.

<Dialog>
  <DialogTrigger asChild>
    <Button variant="danger">Delete project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Delete this project?</DialogTitle>
      <DialogDescription>
        This permanently removes the project and all of its data.
      </DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="outline">Cancel</Button>
      </DialogClose>
      <Button variant="danger">Delete</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>

Props

Dialog props. All native attributes of the underlying element are also accepted and forwarded.
PropTypeDefaultDescription
openbooleanControlled open state. Omit for uncontrolled use.
onOpenChange(open: boolean) => voidCalled when the open state should change.
defaultOpenbooleanInitial open state when uncontrolled.
size"sm" | "md" | "lg" | "xl""md"Maximum width, set on DialogContent.
showCloseButtonbooleantrueRenders the built-in close button. Set on DialogContent.

Accessibility

  • Focus moves into the dialog on open and returns to the trigger on close.
  • Tab is trapped inside while open.
  • Escape closes the dialog.
  • Content outside is marked `aria-hidden`, so screen readers cannot wander out.
  • Always render a DialogTitle — without it the dialog is announced only as “dialog”.
  • Body scroll is locked while open.