Modal
A centered overlay dialog. Built on Radix Dialog with focus trap, escape-to-close, and backdrop click.
Import
import {
Modal,
ModalTrigger,
ModalContent,
ModalHeader,
ModalTitle,
ModalDescription,
ModalBody,
ModalFooter,
ModalClose,
} from '@aspect/react';
Usage
<Modal>
<ModalTrigger asChild>
<Button>Open</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>Confirm action</ModalTitle>
<ModalDescription>This cannot be undone.</ModalDescription>
</ModalHeader>
<ModalBody>
<p>Are you sure?</p>
</ModalBody>
<ModalFooter>
<ModalClose asChild>
<Button variant="ghost">Cancel</Button>
</ModalClose>
<Button variant="destructive">Delete</Button>
</ModalFooter>
</ModalContent>
</Modal>
Compound components
| Part | Purpose |
| ------------------ | -------------------------------- |
| Modal | Root — manages open/close state |
| ModalTrigger | Opens the dialog when clicked |
| ModalContent | The overlay + dialog container |
| ModalHeader | Title area with bottom border |
| ModalTitle | Heading with aria-labelledby |
| ModalDescription | Subtitle with aria-describedby |
| ModalBody | Scrollable content area |
| ModalFooter | Action buttons area |
| ModalClose | Closes the dialog |
Controlled usage
const [open, setOpen] = React.useState(false);
<Modal open={open} onOpenChange={setOpen}>
<ModalContent>...</ModalContent>
</Modal>;
Guidelines
Do:
- Always include a
ModalTitlefor accessibility - Put the primary action on the right in the footer
- Use
variant="destructive"for irreversible confirmations
Don't:
- Don't nest modals inside modals
- Don't use modals for simple confirmations that could be inline
- Don't put complex forms in modals — use a full page instead
Accessibility
- Focus is trapped inside the dialog while open
- Escape key closes the dialog
- Focus returns to the trigger element on close
aria-labelledbyandaria-describedbyare set automatically