On this page
Confirmation Dialog
Accessible modal flow for destructive actions. Shows trigger → dialog → action logic.
Danger Zone
Deleting your account is permanent.
Visual reference
Storybook captures of this pattern in each framework, light and dark modes. The live demo above runs in React; these confirm the Angular and Vue compositions render the same way.
When to use
- Irreversible actions: deleting an account, revoking an API key, leaving an organization.
- Actions whose blast radius the user might underestimate ("delete project" hides "delete all 4,000 issues").
- When the operation is fast enough that a modal is acceptable — anything > ~3 s should open a separate destination, not a dialog.
When not to use
- Reversible actions with an undo affordance — show an AtlToast with an "Undo" action instead.
- Form-style multi-field confirmation ("type the project name to confirm") — that's a destructive form, render it on its own page or in an AtlDrawer.
- As an "are you sure?" wrapper around every save button. Confirmation fatigue trains users to click through.
Accessibility
- AtlDialog uses the native `<dialog>` element with `cdkTrapFocus` — focus moves to the first focusable inside, restores on close. Don't reimplement.
- Escape-to-close comes from the native `<dialog>` element's cancel event — the component intercepts it and routes it through the `open` binding as a close request. If an in-flight operation must not be interrupted, ignore that request (keep `open` true) until the mutation settles.
- The destructive button must remain the *secondary* visual call to action — Cancel as `outline`, "Yes, delete" as `primary`. Reversing colour just to make red prominent is a contrast trap.
See the accessibility overview for the site-wide WCAG stance and per-component focus / ARIA notes.
Common pitfalls
What an LLM most commonly produces wrong here.
- LLMs frequently add `autoFocus` to the destructive button — this nudges users into an accidental confirm. The first Tab stop should be Cancel.
- Forgetting to gate the trigger when the action is already in flight produces double-deletes; flip the dialog's `open` to false only after the mutation resolves.
- Putting body content as a raw string inside `AtlDialogContent` skips the AtlAlert composition the cookbook recommends — the Alert is what carries the warning role for assistive tech.
Variations
With type-to-confirm input
Add an AtlInput inside `AtlDialogContent` that the user must type the resource name into. Disable the destructive button until match.
Loading state
Bind `loading={pending}` on the destructive AtlButton; keep the dialog open so a slow network failure doesn't lose context.
Code
Snippets are intentionally trimmed for readability. The full implementation — with state, styles, and demo data — lives in the framework Storybook files linked below.
<atl-button variant="primary" (click)="isOpen.set(true)">Delete account</atl-button>
<atl-dialog [(open)]="isOpen" size="sm"> <atl-dialog-header>Delete Account</atl-dialog-header> <atl-dialog-content> <atl-alert variant="warning">This action cannot be undone.</atl-alert> </atl-dialog-content> <atl-dialog-footer> <atl-button variant="outline" (click)="isOpen.set(false)">Cancel</atl-button> <atl-button variant="primary" (click)="confirm()">Yes, delete</atl-button> </atl-dialog-footer></atl-dialog><AtlButton onClick={() => setOpen(true)}>Delete account</AtlButton>
<AtlDialog open={open} onOpenChange={setOpen} size="sm"> <AtlDialogHeader>Delete Account</AtlDialogHeader> <AtlDialogContent> <AtlAlert variant="warning">This action cannot be undone.</AtlAlert> </AtlDialogContent> <AtlDialogFooter> <AtlButton variant="outline" onClick={() => setOpen(false)}>Cancel</AtlButton> <AtlButton variant="primary" onClick={confirm}>Yes, delete</AtlButton> </AtlDialogFooter></AtlDialog><AtlButton variant="primary" @click="isOpen = true">Delete account</AtlButton>
<AtlDialog v-model:open="isOpen" size="sm"> <AtlDialogHeader>Delete Account</AtlDialogHeader> <AtlDialogContent> <AtlAlert variant="warning">This action cannot be undone.</AtlAlert> </AtlDialogContent> <AtlDialogFooter> <AtlButton variant="outline" @click="isOpen = false">Cancel</AtlButton> <AtlButton variant="primary" @click="confirm">Yes, delete</AtlButton> </AtlDialogFooter></AtlDialog>Open in Storybook
The same composition with state, styles, and demo data — running live in each framework Storybook.