# Alert dialog

A dialog that stops for a decision, in Candor's theme (Base UI's AlertDialog). It can't be dismissed by clicking outside, Escape answers Cancel, and focus starts on Cancel, so no key press by accident confirms the destructive action. The action's colour comes from the app (`variant="destructive"`); nothing is chosen for the person.

- Registry name: `alert-dialog` (`@candor/alert-dialog`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/alert-dialog

## Install

```bash
npx candor-ui add alert-dialog
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/alert-dialog`.
It brings in: button, layer, locked, motion, tokens, utils.
npm packages: @base-ui/react@^1.9.0, lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { Button } from "@/components/candor/ui/button"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogTrigger,
} from "@/components/candor/ui/alert-dialog"
import { TooltipProvider } from "@/components/candor/ui/tooltip"

export default function Example() {
  return (
    <TooltipProvider>
      <AlertDialog>
        <AlertDialogTrigger render={<Button variant="outline">Delete project</Button>} />
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>Delete the project “Atlas”?</AlertDialogTitle>
            <AlertDialogDescription>Its 4 environments and 18 members lose access straight away.</AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Keep project</AlertDialogCancel>
            {/* Focus starts on Cancel. The action says what can't be undone, in the app's words. */}
            <AlertDialogAction variant="destructive" irreversible="The 312 runs and their logs are deleted for good. Members and settings can be restored from the audit log.">
              Delete project
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </TooltipProvider>
  )
}
```

## <AlertDialog>

No props.

Also takes the props of `AlertDialogPrimitive.Root.Props`.

## <AlertDialogAction>

Props:

- `render?: React.ReactElement | undefined`: Draws this element instead of Candor's button (a link, say); it closes the dialog when pressed.
- `variant?: ButtonVariant | undefined` (default `"default"`): The button's look. Defaults to the primary button; pass `destructive` for an action that removes or can't be undone.
- `locked?: string | undefined`: Why the action can't be taken yet. When set, the button stays focusable and marked `aria-disabled`, pressing it does nothing and keeps the dialog open, and the reason is shown on hover and focus and read to screen readers.
- `irreversible?: string | undefined`: What can't be undone, in the app's words ("The 18,240 rows in this table are deleted for good."). Shown in the footer above the buttons and read with the action. Without it the dialog says nothing about reversibility, so pass it whenever the action is destructive and the app knows.

Also takes the props of `Omit<Classed<AlertDialogPrimitive.Close.Props>, "render">`.

## <AlertDialogCancel>

Props:

- `render?: React.ReactElement | undefined`: Draws this element instead of Candor's outline button; it closes the dialog when pressed.

Also takes the props of `Omit<Classed<AlertDialogPrimitive.Close.Props>, "render">`.

## <AlertDialogContent>

Props:

- `className?: string | undefined`: Classes for the part, merged with Candor's.
- `style?: React.CSSProperties | undefined`: Inline styles for the part, merged over its motion chain.

Also takes the props of `Omit<P, "className" | "style">`.

## <AlertDialogDescription>

Props:

- `className?: string | undefined`: Classes for the part, merged with Candor's.
- `style?: React.CSSProperties | undefined`: Inline styles for the part, merged over its motion chain.

Also takes the props of `Omit<P, "className" | "style">`.

## <AlertDialogFooter>

No props.

Also takes the props of `React.ComponentProps<"div">`.

## <AlertDialogHeader>

No props.

Also takes the props of `React.ComponentProps<"div">`.

## <AlertDialogOverlay>

Props:

- `className?: string | undefined`: Classes for the part, merged with Candor's.
- `style?: React.CSSProperties | undefined`: Inline styles for the part, merged over its motion chain.

Also takes the props of `Omit<P, "className" | "style">`.

## <AlertDialogPortal>

No props.

Also takes the props of `Omit<AlertDialogPrimitive.Portal.Props, "container">`.

## <AlertDialogTitle>

Props:

- `className?: string | undefined`: Classes for the part, merged with Candor's.
- `style?: React.CSSProperties | undefined`: Inline styles for the part, merged over its motion chain.

Also takes the props of `Omit<P, "className" | "style">`.

## <AlertDialogTrigger>

Props:

- `className?: string | undefined`: Classes for the part, merged with Candor's.
- `style?: React.CSSProperties | undefined`: Inline styles for the part, merged over its motion chain.

Also takes the props of `Omit<P, "className" | "style">`.

## Motion

- **open** (overlay, scale): The dialog fades in over the overlay family's duration (200 ms in, 120 ms out by default) and scales up from 0.95 from where the trigger was pressed. Opened any other way (a shortcut, the app's own state) it opens from its centre.

Set with `--cd-motion-alert-dialog-<moment>-*` in your brand file; the families are in the design reference.

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
