ConfirmModal
ImplementedDestructive-confirmation dialog — thin composition over the Modal molecule that owns the cancel/confirm action pair (danger variant by default, primary for archive/apply/clear) while the body (message, warnings, type-to-confirm input) stays consumer-owned. Extracted from 15 dashboard confirm sites (W2).
components/organisms/ConfirmModal/ConfirmModal.tsxDestructive-confirmation dialog — a thin composition over the Modal molecule that owns the cancel/confirm action pair (danger variant by default, primary for archive/apply/clear) while the body (message, warnings, type-to-confirm input) stays consumer-owned. Extracted from 15 dashboard confirm sites (W2).
ConfirmModal
Organism — Account & Admin family
Controlled confirm dialog. The organism owns the action pair — confirm is type="button" so it never submits an outer form — and the Modal molecule keeps the dialog chrome (portal, scrim, focus trap, ESC, scroll lock). Danger variant by default; pass confirmVariant="primary" for non-destructive confirmations. Omit cancelLabel for a single-action dialog (validate/acknowledge) with actionsAlign="center".
This component supports light and dark mode.
Usage
ConfirmModal is a controlled component — pair open with the id/state of the thing being confirmed and always wire onClose. The body stays yours: confirmation message, warning callouts, type-to-confirm inputs, error alerts. All strings are caller-owned (i18n).
import { ConfirmModal } from '@epasslive/design-system'
<ConfirmModal
open={!!target}
onClose={close}
title={t('deleteConfirmTitle')}
cancelLabel={t('cancel')}
confirmLabel={t('delete')}
confirmLoading={deleting}
onConfirm={confirmDelete}
>
<p>{t('deleteConfirm')}</p>
</ConfirmModal>Variant discipline
confirmVariant defaults to "danger" — the canonical delete / detach / cancel-event confirm. Pass confirmVariant={"primary"} for non-destructive confirmations (archive, apply, clear); never pair destructive labels with the primary variant — that mismatches the affordance. For type-to-confirm flows, arm confirmDisabled from your input state and keep closeOnScrimClick={false} while the destructive request is in flight.
Single-action dialogs
Omit cancelLabel for a single-action dialog (validate/acknowledge) and set actionsAlign="center" — the confirm button centers in the footer. The action buttons are type="button", so they never submit an outer form even when the dialog opens from inside one. Use FormModal instead when the dialog needs a real form with submit semantics.
Accessibility
Dialog chrome comes from the Modal molecule — role="dialog", labelled by the title when set, ESC to close (topmost stacked modal only), focus trap with restore, body scroll lock. Pass title or ariaLabel so the dialog is named (WCAG 4.1.2).
Props
open*booleanWhether the dialog is visible.
onClose*() => voidCalled when the dialog should close (ESC, scrim click).
titlestringHeading rendered in the dialog header.
ariaLabelstringAccessible name when no visible heading is used.
size"sm" | "md" | "lg" | "full"'sm'Panel width — sm, the destructive-confirm convention. Pass md/lg for wide confirms (display-info popups).
closeOnScrimClickbooleantrueSet false for confirms that must be actioned explicitly (e.g. while a destructive request is in flight).
children*React.ReactNodeDialog body — confirmation message, warning callouts, type-to-confirm inputs, error alerts.
cancelLabelReact.ReactNodeCancel button label (i18n). Omit for a single-action dialog (validate/acknowledge).
onCancel() => voidonCloseCalled when cancel is pressed.
cancelDisabledbooleanDisable the cancel button (e.g. while deleting).
confirmLabel*React.ReactNodeConfirm button label (i18n).
onConfirm*() => voidCalled when confirm is pressed.
confirmVariant"primary" | "secondary" | "ghost" | "danger"'danger'Danger by default (delete/detach/cancel-event). Pass primary for non-destructive confirmations (archive, apply, clear).
confirmDisabledbooleanDisable the confirm button (e.g. type-to-confirm not yet armed).
confirmLoadingbooleanAsync confirm — also disables the button, guarding double-submits.
actionsSize"sm" | "md" | "lg"'sm'Size for both action buttons — sm, the confirm convention.
actionsAlign"end" | "center"'end'Footer alignment — end (cancel + confirm pair) or center (single-action acknowledge/validate rows).
Props
ConfirmModal
openboolean—Whether the dialog is visible.
onClose() => void—Called when the dialog should close (ESC, scrim click).
titlestring—Heading rendered in the dialog header.
ariaLabelstring—Accessible name when no visible heading is used.
size"sm" | "md" | "lg" | "full"'sm'Panel width — sm, the destructive-confirm convention. Pass md/lg for wide confirms (display-info popups).
closeOnScrimClickbooleantrueSet false for confirms that must be actioned explicitly (e.g. while a destructive request is in flight).
childrenReact.ReactNode—Dialog body — confirmation message, warning callouts, type-to-confirm inputs, error alerts.
cancelLabelReact.ReactNode—Cancel button label (i18n). Omit for a single-action dialog (validate/acknowledge).
onCancel() => voidonCloseCalled when cancel is pressed.
cancelDisabledboolean—Disable the cancel button (e.g. while deleting).
confirmLabelReact.ReactNode—Confirm button label (i18n).
onConfirm() => void—Called when confirm is pressed.
confirmVariant"primary" | "secondary" | "ghost" | "danger"'danger'Danger by default (delete/detach/cancel-event). Pass primary for non-destructive confirmations (archive, apply, clear).
confirmDisabledboolean—Disable the confirm button (e.g. type-to-confirm not yet armed).
confirmLoadingboolean—Async confirm — also disables the button, guarding double-submits.
actionsSize"sm" | "md" | "lg"'sm'Size for both action buttons — sm, the confirm convention.
actionsAlign"end" | "center"'end'Footer alignment — end (cancel + confirm pair) or center (single-action acknowledge/validate rows).
<ConfirmModal />Accessibility
Type
1 ruleARIA
1 ruleUsage
Import
import { ConfirmModal } from "@epasslive/design-system"Example 1
<ConfirmModal open={!!target} onClose={close} title={t('deleteConfirmTitle')} cancelLabel={t('cancel')} confirmLabel={t('delete')} confirmLoading={deleting} onConfirm={confirmDelete}><p>{t('deleteConfirm')}</p></ConfirmModal>Anti-patterns
— Do not use for full form editing — use FormModal (it owns the form + submit semantics)
— Do not pass destructive labels with confirmVariant="primary" — mismatched affordance