Modal

Accessible dialog built on Radix UI. Focus-trapped, keyboard-dismissible.

Basic modal

<Modal>
<ModalTrigger asChild>
<Button>Open modal</Button>
</ModalTrigger>
<ModalContent>
<ModalHeader>
<ModalTitle>Confirm reservation</ModalTitle>
<ModalDescription>Description text here.</ModalDescription>
</ModalHeader>
<ModalFooter>
<ModalClose asChild>
<Button variant="ghost" size="sm">Cancel</Button>
</ModalClose>
<Button size="sm">Confirm</Button>
</ModalFooter>
</ModalContent>
</Modal>

Form in a modal

Fields sit directly on the dialog — no nested panel, since the dialog is already a surface. Note the Date Picker and Select: their popovers are overlays opening above another overlay, which is a legitimate z-layer, not stacked glass.

<ModalContent>
<ModalHeader></ModalHeader>
<div className="flex flex-col gap-4">
<Input label="Member name" />
<DatePicker label="Arrival" value={arrival} onValueChange={setArrival} />
<div className="flex flex-col gap-1.5">
<span className="text-sm font-medium text-ink-soft">Campsite</span>
<Select value={site} onValueChange={setSite}>
<SelectTrigger aria-label="Campsite">
<SelectValue placeholder="Choose a site" />
</SelectTrigger>
<SelectContent></SelectContent>
</Select>
</div>
<FileInput label="Signed agreement" accept=".pdf" buttonLabel="Choose PDF" />
<Textarea label="Notes" />
</div>
<ModalFooter></ModalFooter>
</ModalContent>