Modal
Dialog overlay for confirmations, forms, and focused tasks, built on Radix UI.
Installation
npx paubha@latest add modalUsage
import {
Modal,
ModalBody,
ModalClose,
ModalContent,
ModalDescription,
ModalFooter,
ModalHeader,
ModalTitle,
ModalTrigger,
} from "@/components/ui/modal";
<Modal>
<ModalTrigger asChild>
<Button destructive>Delete item</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>Delete item?</ModalTitle>
<ModalClose />
</ModalHeader>
<ModalBody>
<ModalDescription>This action cannot be undone.</ModalDescription>
</ModalBody>
<ModalFooter>
<Button variant="secondary">Cancel</Button>
<Button destructive>Confirm</Button>
</ModalFooter>
</ModalContent>
</Modal>The close button sits inside ModalHeader as a flex sibling of the title
(ModalTitle is flex-1, ModalClose is shrink-0). That's what the real
spec shows, not the more common absolutely-positioned-corner-X pattern. The
overlay backdrop is bg-bg-overlay (black at 60% in light mode, 70% in dark)
and sits on the --z-overlay layer, with the panel above it on --z-modal.
Both fade in over duration/normal (200ms) with ease-out; under
prefers-reduced-motion they appear instantly.
Pick the smallest size that comfortably fits the task: sm, md (default),
or lg.
API reference
Modal and ModalTrigger are direct re-exports of Radix Dialog's Root/Trigger. Compose ModalHeader, ModalTitle, ModalClose, ModalBody, ModalDescription, and ModalFooter inside ModalContent. Cancel/Confirm buttons are real <Button> instances you compose in, not baked into Modal itself.
ModalContent
| Prop | Type | Default | Description |
|---|---|---|---|
| size | "sm" | "md" | "lg" | "md" | Max width: 400 / 560 / 720px. Use the smallest size that fits the task. |
Accessibility
Accessibility
Role.
role="dialog"witharia-modal="true", set explicitly onModalContentrather than left to Radix's default, since the installed Radix version doesn't set it automatically (verified directly by inspecting the rendered DOM, not assumed).Labelling.
aria-labelledbypoints atModalTitle,aria-describedbyatModalDescription, wired automatically by Radix.Focus. Moves inside on open; Tab and Shift+Tab stay within the modal. Background content is inert while it's open.
Dismiss. Escape closes it and returns focus to the trigger, as does clicking
ModalCloseor the overlay. The icon-only close control is labelled "Close".Motion. 200ms ease-out fade on open; instant when the user prefers reduced motion.