ConfirmDialog
Pre-configured dialog for confirming a single, often destructive action. Replaces window.confirm() with a styleable, focus-trapped modal that supports async onConfirm handlers.
Playground
<script lang="ts">
import { ConfirmDialog } from '@urbicon-ui/blocks';
let open = $state(false);
</script>
<ConfirmDialog
bind:open
cancelLabel="Cancel"
confirmLabel="Delete"
description="This cannot be undone."
intent="danger"
title="Delete project?"
/>01 Examples
Destructive confirmation
<script>
let confirmOpen = $state(false);
</script>
<Button intent="danger" onclick={() => (confirmOpen = true)}>Delete</Button>
<ConfirmDialog
bind:open={confirmOpen}
title="Delete project?"
description="This cannot be undone."
intent="danger"
confirmLabel="Delete"
onConfirm={() => deleteProject()}
/>Async onConfirm — auto-loading
<ConfirmDialog
bind:open
title="Submit report?"
description="Network call may take a moment."
intent="primary"
confirmLabel="Submit"
onConfirm={async () => { await api.submit(); }}
onError={() => toaster.danger('Could not submit report')}
/>02 Customization
intent drives both the dialog accent strip and the
confirm-button colour. The neutral intent upgrades the
confirm button to primary for visual affordance —
override via confirmIntent if your design system needs different
tones.
For confirmations with richer markup (a list of consequences, a typed-confirmation field),
pass content through the default children snippet — it
renders below the description.
ConfirmDialog is a pre-configured Dialog and owns no styles of its own: class, unstyled, slotClasses, and preset are forwarded verbatim to the inner Dialog. slotClasses therefore takes the Dialog slot keys (panel, header, title, body, footer, backdrop, …), and presets are registered under the presets.Dialog key on BlocksProvider — a Dialog preset restyles both
components. See Customization.
03 Accessibility
Inherited from Dialog
Focus trap, Escape-to-cancel and backdrop-click-to-cancel all come from the underlying Dialog. While an async onConfirm is pending all three are disabled, so the user
cannot navigate away mid-action.
Real buttons
Cancel and confirm are actual <button> elements —
keyboard navigation (Tab, Enter, Space) works without extra wiring.
Translatable labels
Default labels resolve via bt('button.confirm') / bt('button.cancel') — translate them through the i18n package, or override per call via confirmLabel / cancelLabel.
04 API Reference
Prop | Type | Default | Description | |
|---|---|---|---|---|
title required | string | — | Heading shown in the dialog header. | |
cancelLabel | string | — | Label of the cancel button. Defaults to the localized button.cancel. | |
children | Snippet | — | Optional richer markup rendered below description. | |
class | string | — | Extra classes merged onto the dialog element. Forwarded to DialogProps.class. | |
closeOnBackdropClick | boolean | true | Whether the backdrop click cancels. | |
closeOnEscape | boolean | true | Whether Escape cancels. | |
confirmIntent | ConfirmIntent | — | Override for the confirm button intent. Defaults to ConfirmDialogProps.intent,
with neutral upgraded to primary for visual affordance. | |
confirmLabel | string | — | Label of the confirm button. Defaults to the localized button.confirm. | |
description | string | — | Description rendered above the footer. Use children for richer markup. | |
intent | DialogIntent | 'danger' | Accent on the dialog header strip. Drives the default confirmIntent. | |
loading | boolean | false | Externally controlled loading flag. Combined with the internal busy
flag from an async onConfirm. While truthy, both buttons are
disabled and dismissal is blocked. | |
onCancel | () => void | — | Fired when the user cancels (button, backdrop, or Escape). | |
onConfirm | () => void | Promise<void> | — | Confirm handler. May return a promise — the dialog stays open and shows
a loading state while it resolves, then auto-closes on success. If the
promise rejects the dialog stays open and re-enables; the rejection is
reported via ConfirmDialogProps.onError. | |
onError | (error: unknown) => void | — | Fired when an async onConfirm rejects (or a sync one throws). The
dialog stays open and re-enables so the user can retry or cancel — use
this to surface the failure (toast, inline message). Without a handler
the rejection is logged DEV-only (console.error) and swallowed in
production; it never escapes as an unhandled promise rejection. | |
open | boolean | — | Controls visibility. Supports bind:open. | |
preset | string | — | Apply a named preset registered via <BlocksProvider presets={{ Dialog: {...} }}>.
Resolved against the Dialog component key — ConfirmDialog shares the
Dialog preset space instead of introducing a parallel one. | |
slotClasses | Partial<Record<DialogSlots, string>> | — | Per-slot class overrides, forwarded to the underlying Dialog. Slots: dialog | backdrop | panel | content | header | title | body | footer. | |
transitionDuration | number | — | Override the enter/exit animation duration in milliseconds, forwarded to
the underlying Dialog. Defaults to the overlay token
--blocks-overlay-enter-duration / --blocks-overlay-exit-duration
(200ms / 180ms). Respects prefers-reduced-motion. | |
transitionEasing | (t: number) => number | — | Override the enter/exit easing function, forwarded to the underlying
Dialog. Defaults to the overlay token easing (quintOut). | |
unstyled | boolean | false | Strip the underlying Dialog's default styles. Combine with slotClasses
for a fully custom appearance. | |
...HTMLDialogAttributes inherited | HTMLAttributes | — | HTML attributes (excluding: 'children' | 'open' | 'title' | 'draggable') |
05 Types
Local type definitions used by this component.
Name | Kind | Category | Used by | Description | |
|---|---|---|---|---|---|
ConfirmIntent | type | helper | 1 | Intent applied to the confirm button. Reuses the standard intent palette
via DialogIntent (with neutral mapped to primary for visual
affordance). | |
ConfirmDialogProps | interface | props | 0 | — | |
DialogIntent | type | helper | 1 | Semantic intent of the dialog. Tints the header markers (title, icon) and is
mirrored on the panel as data-intent. | |
DialogSlots | type | variant | 0 | Slot names derived from the tv() config above — single source of truth for slotClasses. | |
DialogVariants | type | variant | 0 | — | |
SlotNames | type | helper | 0 | Extracts the slot-name union from a slotted tv() config function — the
companion to VariantProps. The slot-mode overload returns
(props?) => { [K in keyof S]: SlotFn }, so keyof ReturnType<T> is exactly
the set of slot names a component declares in tv({ slots: … }).
Use it to type a component's slotClasses prop from the single source of
truth (its *.variants.ts) instead of hand-maintaining a parallel union
that silently drifts when a slot is added or renamed: | |
VariantProps | type | helper | 0 | — |
06 Installation
Import
import { ConfirmDialog } from '@urbicon-ui/blocks';