Modal
Overviewimport { Modal, StatusModal } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Basic
Controlled by open and onOpenChange. It portals to the body, traps focus while open, and restores focus to the trigger on close.
import { Modal, Button } from "@nili/ui"
export function Example() {
const [open, setOpen] = useState(false)
return (
<>
<Button onClick={() => setOpen(true)}>Open</Button>
<Modal
open={open}
onOpenChange={setOpen}
title="Invite a teammate"
description="They will get an email with a link to join."
closeLabel="Close"
footer={
<>
<Button appearance="stroke" onClick={() => setOpen(false)}>Cancel</Button>
<Button onClick={() => setOpen(false)}>Send invite</Button>
</>
}
>
<TextInput label={t('invite.email')} placeholder="hello@nili.design" />
</Modal>
</>
)
}Sizes
Four panel widths. headerSize is separate — Figma's Medium (80) and Small (56) header.
import { Modal } from "@nili/ui"
export function Example() {
return (
<>
<Modal size="sm" … />
<Modal size="md" … />
<Modal size="lg" … />
<Modal size="xl" … />
</>
)
}Header and alignment
icon is a free leading slot and wins over status. A vertical alignment centres the icon, title and text — the shape a confirmation wants.
import { Modal, Icon } from "@nili/ui"
import { RiDeleteBinLine } from "@remixicon/react"
export function Example() {
return (
<Modal
open={open}
onOpenChange={setOpen}
alignment="vertical"
status="error"
icon={<Icon icon={RiDeleteBinLine} />}
title="Delete this project?"
description="This cannot be undone. Everything in it will be removed."
stretchFooter
footer={
<>
<Button appearance="stroke" onClick={close}>Cancel</Button>
<Button color="error" onClick={close}>Delete</Button>
</>
}
/>
)
}Status Modal
The same modal with the status fixed: it picks the KeyIcon and its colour, so a “success” dialogue cannot arrive in red.
import { StatusModal, Button } from "@nili/ui"
export function Example() {
return (
<StatusModal
open={open}
onOpenChange={setOpen}
status="success"
alignment="vertical"
title="Payment received"
description="We have emailed you a receipt."
stretchFooter
footer={<Button onClick={close}>Done</Button>}
/>
)
}Controlling dismissal
closeOnOutsideClick and closeOnEscape default to on. Turn them off for a dialogue holding unsaved work — but then leave the close button, or there is no way out.
import { Modal } from "@nili/ui"
export function Example() {
return (
<Modal
open={open}
onOpenChange={setOpen}
closeOnOutsideClick={false}
closeOnEscape={false}
showClose
closeLabel="Close"
title="Unsaved changes"
…
/>
)
}Modal
extends Omit< ComponentPropsWithoutRef<'div'>, 'title' | 'children' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | |
| aria-label | string | — | |
| open* | boolean | — | |
| onOpenChange* | (open: boolean) => void | — | |
| title | ReactNode | — | |
| description | ReactNode | — | |
| icon | ReactNode | — | Figma header "Right Icon" — a free leading slot. Wins over `status`. |
| status | error | warning | success | info | — | Picks the default KeyIcon and its colour. |
| headerSize | sm | md | sm | Figma header Medium (80) / Small (56). |
| alignment | horizontal | vertical | horizontal | Figma Status Modal alignment. `vertical` centres icon, title and text. |
| size | xl | lg | md | sm | md | |
| footer | ReactNode | — | |
| stretchFooter | boolean | false | |
| showClose | boolean | true | |
| closeLabel | string | Close | |
| closeOnOutsideClick | boolean | true | |
| closeOnEscape | boolean | true | |
| container | HTMLElement | null | — |
ModalBody
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | sm | md | sm |
ModalFooter
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| stretch | 'true' | 'false' | false |
ModalHeader
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | sm | md | sm | |
| alignment | horizontal | vertical | horizontal |
ModalOverlay
internalModalPanel
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | sm | md | lg | xl | md |
StatusModal
extends Omit< ModalProps, 'alignment' | 'status' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| status* | error | warning | success | info | — | |
| alignment | horizontal | vertical | vertical |