The heaviest confirm: an impact preview and a typed phrase; and the Danger zone section that leads to it.
TypeToConfirmModal shows what will change (ImpactPreview), asks for a typed phrase (copyable, case-insensitive by default) and runs an async onConfirm, showing an error in place if it fails.
DangerZone is the section at the foot of a settings or record page that holds destructive actions, one DangerZoneRow each, each opening its confirm.
Installation
npx shadcn@latest add KezLahd/rime/danger-zoneAlso installs: base, button, type-to-confirm.
Usage
import { DangerZone, DangerZoneRow, TypeToConfirmModal } from "@/components/ui";
<DangerZone>
<DangerZoneRow title="Remove team member" actionLabel="Remove team member" confirm={{ title: "Remove Jane Cooper?", phrase: "remove jane cooper", confirmLabel: "Remove team member", onConfirm: remove, impact: ITEMS }} />
</DangerZone>Examples
Open
Remove Jane Cooper?
Danger zone
Danger zone
Actions here can't be undone.
Remove team member
Signs them out everywhere and revokes their access.
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
TypeToConfirmModal
| Prop | Type | Default | Description |
|---|---|---|---|
| openRequired | boolean | ||
| onCloseRequired | () => void | Cancel, Esc, scrim, the close button, and a confirm that went through. | |
| titleRequired | ReactNode | ||
| meta | ReactNode | A quiet line under the title: the record's name or reference. | |
| description | ReactNode | ||
| inline | boolean | Render the open dialog in place (see Modal's inline). | |
| impact | ImpactItem[] | ImpactPreviewProps | What the action does, one row per consequence, in the impact panel above the phrase. Pass the rows, or the full ImpactPreview props for a preview that loads from the server (loading, error, onRetry). | |
| consequences | ReactNode | Prose consequences. Still supported: shown as plain text inside the impact panel, above any rows. Prefer `impact` for new dialogs. | |
| consequencesTitle | ReactNode | "This action cannot be undone" | The compact danger line heading the impact panel. |
| phraseRequired | string | What the user types to arm the button. Keep it specific to the thing being destroyed ("delete atlas redesign"), so muscle memory can't confirm the wrong record. Shown verbatim; lower-case reads best. | |
| caseSensitive | boolean | false | Default false: "Jane Cooper" matches "jane cooper". |
| children | ReactNode | Extra required fields (a training date). Rendered between the impact panel and the phrase. | |
| canConfirm | boolean | true | Validity of the children. The button arms only when this and the phrase both hold. |
| confirmLabelRequired | string | ||
| cancelLabel | string | "Cancel" | |
| busyLabel | string | Replaces the confirm label while onConfirm is in flight, e.g. "Deleting…". | |
| onConfirmRequired | () => void | Promise<unknown> | The irreversible request. While a returned promise is pending the dialog is locked (no Esc, scrim, close or Cancel). When it resolves the dialog closes through onClose; when it rejects the dialog stays open and unlocked, the typed phrase kept, and an error toast says so. | |
| errorTitle | ReactNode | "That didn't go through" | Title of the failure toast. The description always says nothing changed. |
| size | ModalProps["size"] | "md" | |
| mobile | ModalProps["mobile"] |
DangerZone
| Prop | Type | Default | Description |
|---|---|---|---|
| title | ReactNode | "Danger zone" | Default "Danger zone". |
| description | ReactNode | One line under the title: whose data this is, who can see it. | |
| headingLevel | 2 | 3 | 4 | 2 | 2 on a settings page, 3 inside a titled section. |
| children | ReactNode | DangerZoneRow elements. The zone renders nothing without any. | |
| className | string |
DangerZoneRow
| Prop | Type | Default | Description |
|---|---|---|---|
| titleRequired | ReactNode | ||
| description | ReactNode | What it does and what it takes with it. The button names the action; this explains it. | |
| actionLabelRequired | string | A full verb phrase: "Deactivate team member", not "Deactivate". | |
| confirm | Omit<TypeToConfirmModalProps, "open" | "onClose"> | The row owns a TypeToConfirmModal with these props and opens it from the button. It closes when onConfirm resolves. For a confirm whose extra fields the page holds in state, use onAction and render the modal there. | |
| onAction | () => void | Called instead of opening the built-in confirm. | |
| disabled | boolean | ||
| disabledReason | ReactNode | Shown in the row while disabled: why, and what would enable it. |
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
This component has no tokens of its own; it reads the system tokens below.
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --danger-fill | tokens.css | … |
| --danger-ink | tokens.css | … |
| --danger-line | tokens.css | … |
| --ease-out | tokens.css | … |
| --font-body | tokens.css | … |
| --font-mono | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-heading | tokens.css | … |
| --ink-muted | tokens.css | … |
| --ink-secondary | tokens.css | … |
| --line | tokens.css | … |
| --line-soft | tokens.css | … |
| --r-xl | tokens.css | … |
| --shadow-input | tokens.css | … |
| --success-fill | tokens.css | … |
| --success-ink | tokens.css | … |
| --success-solid | tokens.css | … |
| --surface-solid | tokens.css | … |
| --text-body | tokens.css | … |
| --text-caption | tokens.css | … |
| --text-card-title | tokens.css | … |
| --text-small | tokens.css | … |
| --wash-active | tokens.css | … |
Accessibility
- The confirm button stays disabled until the phrase matches; progress toward the phrase is announced through a polite status region.
Do and don't
Do
- Make the phrase name the thing being destroyed.
Don't
- Don't use it for everyday deletes; ConfirmModal is enough.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open type-to-confirm.md