# TypeToConfirmModal and DangerZone

> The heaviest confirm: an impact preview and a typed phrase; and the Danger zone section that leads to it.

Category: Overlays. Docs page: https://rime.mjsons.net/components/type-to-confirm

## Installation

```bash
npx shadcn@latest add KezLahd/rime/type-to-confirm
```

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.

## Usage

```tsx
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>
```

## Props: TypeToConfirmModal

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `open` (required) | `boolean` |  |  |
| `onClose` (required) | `() => void` |  | Cancel, Esc, scrim, the close button, and a confirm that went through. |
| `title` (required) | `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. |
| `phrase` (required) | `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. |
| `confirmLabel` (required) | `string` |  |  |
| `cancelLabel` | `string` | `"Cancel"` |  |
| `busyLabel` | `string` |  | Replaces the confirm label while onConfirm is in flight, e.g. "Deleting…". |
| `onConfirm` (required) | `() => 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"]` |  |  |

## Props: 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` |  |  |

## Props: DangerZoneRow

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` (required) | `ReactNode` |  |  |
| `description` | `ReactNode` |  | What it does and what it takes with it. The button names the action; this explains it. |
| `actionLabel` (required) | `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. |

## Examples

### Open

The confirm itself, rendered in place with inline.

```tsx
<TypeToConfirmModal inline open onClose={close} title="Remove Jane Cooper?" meta="Team member · Design" phrase="remove jane cooper" confirmLabel="Remove team member" impact={ITEMS} onConfirm={remove} />
```

### Danger zone

```tsx
<DangerZone>…</DangerZone>
```

## Theme tokens

System tokens it reads: `--danger-fill`, `--danger-ink`, `--danger-line`, `--ease-out`, `--font-body`, `--font-mono`, `--ink-body`, `--ink-heading`, `--ink-muted`, `--ink-secondary`, `--line`, `--line-soft`, `--r-xl`, `--shadow-input`, `--success-fill`, `--success-ink`, `--success-solid`, `--surface-solid`, `--text-body`, `--text-caption`, `--text-card-title`, `--text-small`, `--wash-active`.

## Accessibility

- The confirm button stays disabled until the phrase matches; progress toward the phrase is announced through a polite status region.

## Do

- Make the phrase name the thing being destroyed.

## Don't

- Don't use it for everyday deletes; ConfirmModal is enough.
