Alert Dialog
A confirmation modal that cannot be dismissed by accident — the same native <dialog> foundation as Dialog, with every dismissal path that isn't an explicit choice switched off.
Usage#
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
AlertDialogTrigger,
} from "neelam-ui";
<AlertDialog>
<AlertDialogTrigger>Delete project</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete this project?</AlertDialogTitle>
<AlertDialogDescription>This cannot be undone.</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction variant="destructive">Delete</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>How it differs from Dialog#
Mechanically it is a Dialog — same native
<dialog>, same focus trapping, same top-layer stacking. Three deliberate
subtractions make it an alert dialog:
- No outside-click dismissal. Not a prop that defaults to off — simply never available. A stray click silently discarding a destructive confirmation is exactly what this component exists to prevent.
- No
Escape. A native<dialog>fires a cancelablecancelevent beforeEscapecloses it, soonCancelcallspreventDefault()on it. The close is stopped before it starts, rather than closing and reopening. - No corner close button. Every way out should read as a deliberate choice between the two footer buttons, not a third, easier-to-misclick escape hatch.
Reserve it for consequences
An alert dialog takes away every casual way out. That is correct for deleting
an account and hostile for "you have unsaved changes". If dismissing is a
reasonable thing to want, use Dialog.
AlertDialogTrigger, AlertDialogHeader, AlertDialogTitle,
AlertDialogDescription, and AlertDialogFooter are the exact same components
as their Dialog counterparts, re-exported under alert-flavoured names. Only
AlertDialogContent, AlertDialogAction, and AlertDialogCancel are new.
Action and Cancel#
Both close the dialog. They differ only in default styling, so the pair reads as
primary/secondary without either needing an explicit variant:
AlertDialogActiondefaults tovariant="default".AlertDialogCanceldefaults tovariant="outline".
Both accept Button's full variant and size set, which is the whole
affordance for a destructive confirmation:
<AlertDialogAction variant="destructive">Delete</AlertDialogAction>Calling preventDefault() in your own onClick stops the dialog closing — the
hook for keeping it open while an async delete is in flight.
Keyboard#
| Key | Behaviour |
|---|---|
| EnterSpace | Opens the dialog from the trigger; activates the focused footer button when open. |
| TabShift+Tab | Cycles focus within the dialog and cannot leave it. |
| Escape | Deliberately does nothing. Cancel is the way out. |
Escape doing nothing is a real trade-off
It breaks a convention users rely on. That is the point — but it means the cancel button must always be visible and obvious, never scrolled out of view on a small screen.
Accessibility#
- The panel carries
role="alertdialog"rather than the<dialog>element's implicitdialogrole, which is what tells assistive tech this one requires an immediate, explicit response. AlertDialogTitlesupplies the accessible name viaaria-labelledbyand is required.AlertDialogDescriptionis wired up witharia-describedby.- Focus moves into the dialog on open and returns to the trigger on close.
- Background content is made inert by the platform, so it is removed from the accessibility tree rather than merely covered.
- Transitions are dropped under
prefers-reduced-motion.
API reference#
AlertDialog#
| Prop | Type | Default |
|---|---|---|
defaultOpenInitial open state when uncontrolled. Defaults to `false`. | boolean | — |
onOpenChangeCalled whenever the open state changes, whether from `DialogTrigger`, `DialogClose`, the built-in close button, Escape, or an outside click. | ((open: boolean) => void) | — |
openControls the open state. Omit to let the dialog manage its own state. | boolean | — |
AlertDialogContent#
| Prop | Type | Default |
|---|---|---|
onCancel | ReactEventHandler<HTMLDialogElement> | — |
AlertDialogAction#
| Prop | Type | Default |
|---|---|---|
size | "sm" | "md" | "lg" | "icon" | null | — |
variant | "link" | "default" | "secondary" | "outline" | "destructive" | "ghost" | null | outline |
AlertDialogCancel#
| Prop | Type | Default |
|---|---|---|
size | "sm" | "md" | "lg" | "icon" | null | — |
variant | "link" | "default" | "secondary" | "outline" | "destructive" | "ghost" | null | outline |