Modal
Accessible dialog with focus trap, Escape to close, scroll lock, and optional footer.
Basic
import { useState } from 'react'
import { Modal, Button } from '@canarist/ui'
function Example() {
const [open, setOpen] = useState(false)
return (
<>
<Button onClick={() => setOpen(true)}>Open Modal</Button>
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="modal-title"
>
<h2 id="modal-title">Modal Title</h2>
<p>Modal content goes here.</p>
</Modal>
</>
)
}With footer
<Modal
open={open}
onClose={() => setOpen(false)}
aria-labelledby="modal-title"
footer={
<div className="flex justify-end gap-2">
<Button variant="ghost" onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => setOpen(false)}>Confirm</Button>
</div>
}
>
<h2 id="modal-title">Confirm Action</h2>
<p>Are you sure you want to continue?</p>
</Modal>Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Whether the modal is visible. |
| onClose | () => void | — | Called when clicking the backdrop, close button, or pressing Escape. |
| children | ReactNode | — | Scrollable body content. |
| footer | ReactNode | — | Sticky footer area, typically action buttons. |
| aria-labelledby | string | — | ID of the element labeling the dialog (required for accessibility). |
| aria-describedby | string | — | ID of the element describing the dialog. |
| className | string | — | Additional CSS classes on the dialog panel. |