# Dialog

A modal window above a scrim, in five widths. Focus moves into it when it opens and goes back to the trigger when it closes. It uses shadow-high, the shadow kept for floating layers.

## Usage

### When to use

- A short task that should block the page until it is done or cancelled, such as renaming a project.
- A confirmation that is not destructive.
- A task that needs room, such as a table or a preview, with a larger size.

### When not to use

- Confirming a destructive action. Use AlertDialog.
- Long forms or reading. Give them their own page.
- Opening a dialog from a dialog. Close the first one before the next one opens, or give the flow its own page.

## Variants

### size

Set on DialogContent. Widest the panel gets: sm 448px (short confirmations), md 512px (forms of a few fields), lg 672px, xl 896px (tables, previews), full the whole viewport minus spacing-lg on each side, in height too. On a narrow screen every size fills the width minus spacing-lg.

Values: `sm`, `md`, `lg`, `xl`, `full`. Default: `md`.

## Examples

### Default

```tsx
<Dialog>
  <DialogTrigger asChild>
    <Button>Rename project</Button>
  </DialogTrigger>
  <DialogContent>
    <DialogHeader>
      <DialogTitle>Rename project</DialogTitle>
      <DialogDescription>Pick a name your team will recognize.</DialogDescription>
    </DialogHeader>
    <DialogFooter>
      <DialogClose asChild>
        <Button variant="secondary">Cancel</Button>
      </DialogClose>
      <Button>Save</Button>
    </DialogFooter>
  </DialogContent>
</Dialog>
```

## Do and don't

### Do

- Use a dialog for a short task or a confirmation that should block the page until done or cancelled.
- Pick the smallest size that fits the content: sm for a sentence and two buttons, md for a short form, lg or xl for tables and previews. Long content scrolls inside the panel, which stays spacing-lg from the viewport edges.
- Let Escape, the close button and a scrim click close it.
- Open and close without animation (current behavior). If an animation is added later, reduced motion gets a fade only.
- Return focus to the trigger on close.

### Don't

- Don't use Dialog to confirm a destructive action. Use AlertDialog, which focuses Cancel first and doesn't close on a scrim click.
- Don't open a dialog from a dialog.
- Don't use a dialog for long forms or reading; use a page or sheet. A bigger size does not make a long form short.
- Don't use size full for a short task; the user loses sight of the page they came from.
- Don't leave out DialogTitle. If it shouldn't show, make it sr-only.
- Don't remove shadow-high from the panel. A dialog floats above the page, so a border alone isn't enough.

## Props

| Name | Type | Values | Default | Description |
| --- | --- | --- | --- | --- |
| `open` | `boolean` |  |  | Set on Dialog. Whether the dialog is open, when you control it. Pair it with onOpenChange. |
| `defaultOpen` | `boolean` |  | `false` | Set on Dialog. Opens the dialog on first render, when you don't control it. |
| `onOpenChange` | `function` |  |  | Set on Dialog. Called with true or false when the dialog opens or closes (trigger, close button, Escape, scrim). |
| `modal` | `boolean` |  | `true` | Set on Dialog. true traps focus and hides the rest of the page from assistive tech. Keep it true; false only for a panel the user works next to, which is better as a sheet. |
| `size` | `enum` | `sm`, `md`, `lg`, `xl`, `full` | `md` | Set on DialogContent. Widest the panel gets: sm 448px (short confirmations), md 512px (forms of a few fields), lg 672px, xl 896px (tables, previews), full the whole viewport minus spacing-lg on each side, in height too. On a narrow screen every size fills the width minus spacing-lg. |
| `showCloseButton` | `boolean` |  | `true` | DialogContent: show the corner close button. DialogFooter takes the same prop (default false) to add a secondary Close button. |

### Slots

| Name | Required | Description |
| --- | --- | --- |
| `trigger` | no | Element that opens the dialog. |
| `title` | yes | Dialog title (required for a11y). |
| `description` | no | Optional supporting text. |
| `content` | no | Main body. |
| `footer` | no | Actions. |

## Anatomy

| Part | Description |
| --- | --- |
| `root` | Root. Controls open state. |
| `overlay` | Scrim behind the content. color-overlay-scrim. |
| `content` | The floating panel: raised surface, radius-page, shadow-high, border. |
| `header` | Top block for title and description. |
| `title` | Accessible name of the dialog. |
| `description` | Optional supporting text. |
| `footer` | Actions row at the bottom. |
| `close` | Icon button that dismisses the dialog. |
| `icon` | Tabler XIcon inside the close control, 16px. |
| `hitArea` | Transparent ::before on the close control, never smaller than size-target-min. |

## Accessibility

Role: `dialog`

| Keys | Action |
| --- | --- |
| `Escape` | Closes the dialog and returns focus to the trigger. |
| `Tab` | Cycles focus inside the dialog (focus trap). |

- Traps focus and closes on Escape. Hides the rest of the page from assistive tech (aria-hidden on siblings); it does not set aria-modal.
- Always provide a DialogTitle (visible or sr-only) so the dialog has an accessible name.
- The corner close is a DialogClose with an sr-only "Close" label and a Tabler XIcon. If showCloseButton is false on DialogContent, the footer must have a Cancel or Close.
- Do not nest dialogs.

## Install

```bash
npx shadcn add https://werewolf-ui.vercel.app/r/dialog.json
```

This also adds `styles/werewolf.css`, the tokens every component uses, and any other component it needs. Import that file once in your root CSS, after `@import "tailwindcss"`, with a path relative to that CSS file (from `app/globals.css` it is `@import "../styles/werewolf.css"`).
