DocsPlatform
AlertDialog
AlertDialog is Dialog's confirmation-focused sibling. It keeps the same accessible modal anatomy and focus behaviour, but dismissal is off by default so an accidental Escape or outside click cannot lose a decision.
Explore the layers
Machine is framework-free state. Headless turns that state into accessible parts. Styled applies Grassroot's visual recipe. Vanilla mounts the same contract without a framework.
Install
npm i @grassroot/ui-machines
import { createService } from "@grassroot/statechart"import { alertDialogMachine } from "@grassroot/ui-machines/alert-dialog" const service = createService(alertDialogMachine, { input: { defaultOpen: false }, runCommand: (command) => console.log(command),})service.start()service.send({ type: "OPEN", reason: "programmatic" })// Escape and outside-click are no-ops until dismissable is enabled.Behaviour contract
- Open state: use
openfor controlled state ordefaultOpenfor state owned by the component. - Dismissal:
dismissabledefaults tofalse. Escape and outside click do nothing until it is enabled. - Commands: a change request reports the proposed value and reason; controlled consumers decide when to adopt it.
- No-ops: opening an open dialog or closing a closed dialog produces no change request.
- Forms: none.
Dismissal & focus
When open, AlertDialog enters the modal layer stack, traps focus inside its content, and locks document scrolling. The topmost layer owns Escape and outside-click handling. With the default non-dismissable setting, those events are intentionally ignored; a close trigger or programmatic action remains available.
Anatomy
AlertDialog.Trigger data-part="trigger" optional opener
AlertDialog.Backdrop data-part="backdrop" modal scrim
AlertDialog.Positioner data-part="positioner" fixed overlay wrapper
AlertDialog.Content data-part="content" role="alertdialog" aria-modal="true"
AlertDialog.Title data-part="title" labelled heading
AlertDialog.Description data-part="description" supporting copy
AlertDialog.CloseTrigger data-part="close-trigger" dismissal action
Root owns the service and renders no element. Rendered parts expose
data-scope="alert-dialog", their part name, and open/closed
state. Content mounts only while open and receives label and description
references when those parts are present.
Keyboard
Tab and Shift+Tab stay inside the content while it is open. Escape is handled only for a dismissable AlertDialog; with the default setting it is a no-op. Closing returns focus to the trigger when one exists.
Accessibility
Content uses role="alertdialog" and aria-modal="true".
Titles and descriptions are connected only when rendered, so the panel
never points at an absent id. The modal layer and focus trap are shared
headless managers rather than framework-specific event handlers.
Evidence & further reading
- generated AlertDialog browser and accessibility evidence.
alert-dialog.contract.ts, the shared browser contract.alert-dialog.spec.md, the versioned behaviour and anatomy contract.- Dialog: the modal sibling and DropdownMenu: the non-modal sibling.