# Alert dialog

A modal that asks before something that cannot be undone: a title, what will happen, and a cancel and a confirm action. It closes only on them and Escape.

Block: `smbk-alert-dialog`. Status: proposed. Figma: the Figma file, https://www.figma.com/community/file/1688324308358870630. Desktop and mobile: differ by presentation only.

## Options

| Axis | Values | Default | Meaning |
|---|---|---|---|
| `size` | `sm`, `md` | `sm` | The greatest width the panel takes: the modal’s two narrower widths, since the question is short. |
| `inline` | on / off | off | Shown in place with no scrim, for previews and documentation. |

Parts: `smbk-modal__header`, `smbk-modal__icon`, `smbk-modal__title`, `smbk-modal__body`, `smbk-modal__footer`

## Markup

```html
<dialog
  role="alertdialog"
  open=""
  class="smbk-alert-dialog smbk-modal smbk-modal--size-sm smbk-modal--inline"
  aria-labelledby="alert-dialog-1-title"
  aria-describedby="alert-dialog-1-description"
>
  <div class="smbk-modal__header">
    <i class="ph ph-trash smbk-icon smbk-icon--size-md smbk-modal__icon" aria-hidden="true"></i>
    <h2 id="alert-dialog-1-title" class="smbk-modal__title">Delete this project?</h2>
  </div>
  <div class="smbk-modal__body">
    <p id="alert-dialog-1-description">Website redesign and its 24 pages will be removed for everyone on the team. This cannot be undone.</p>
  </div>
  <hr class="smbk-divider smbk-divider--orientation-horizontal"/>
  <div class="smbk-modal__footer">
    <button type="button" class="smbk-btn smbk-btn--tone-outline smbk-btn--size-md">
      <span class="smbk-btn__label">Cancel</span>
    </button>
    <button type="button" class="smbk-btn smbk-btn--tone-danger smbk-btn--size-md">
      <span class="smbk-btn__label">Delete project</span>
    </button>
  </div>
</dialog>
```

## React

```tsx
import { AlertDialog } from './design-system/components/alert-dialog/alert-dialog.js';

<AlertDialog size="sm" inline open icon="Trash" title="Delete this project?" description="Website redesign and its 24 pages will be removed for everyone on the team. This cannot be undone." confirmLabel="Delete project" />
```

## Stylesheet

```css
/* smbk-alert-dialog — Alert dialog. The modal with the alertdialog role, drawn by the modal's stylesheet: its
 * markup carries the modal's block, size and part classes after its own mark, smbk-alert-dialog, so every rule of
 * modal/modal.css and the shared dialog/dialog.css (the scrim, the motion, the page held still) draws it.
 * This file holds no rules: the alert dialog's own class is only its mark in markup. */
```

## Tokens it reads

```json
{
  "color": {
    "overlay": {
      "scrim": {
        "$type": "color",
        "$value": "{{color.alpha.black.50}}",
        "$description": "Dimmed backdrop behind dialogs and drawers."
      }
    },
    "border": {
      "rim": {
        "$type": "color",
        "$value": "{{color.alpha.white.90}}",
        "$description": "The light rim that makes a surface look lit: fields, chosen pills, cards."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.300}}",
        "$description": "Dividers and quiet edges."
      }
    },
    "surface": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.100}}",
        "$description": "Standard surface: cards, panels, the header."
      }
    },
    "text": {
      "default": {
        "$type": "color",
        "$value": "{{color.grey.900}}",
        "$description": "Main text."
      },
      "subtle": {
        "$type": "color",
        "$value": "{{color.grey.700}}",
        "$description": "Secondary text: descriptions, metadata."
      }
    },
    "feedback": {
      "danger": {
        "icon": {
          "$type": "color",
          "$value": "{{color.red.600}}",
          "$description": "The danger tone’s own icon (an alert sign), so the tone is never shown by colour alone."
        }
      }
    }
  },
  "motion": {
    "exit": {
      "duration": {
        "$type": "duration",
        "$value": "{{duration.200}}"
      },
      "easing": {
        "$type": "cubicBezier",
        "$value": "{{easing.expo-in}}"
      }
    },
    "enter": {
      "duration": {
        "$type": "duration",
        "$value": "{{duration.320}}"
      },
      "easing": {
        "$type": "cubicBezier",
        "$value": "{{easing.expo-out}}"
      }
    }
  },
  "space": {
    "8": {
      "$type": "dimension",
      "$value": "8px",
      "$description": "Raw space step, 8 px. Only for fixed distances inside a component; spacing that follows density uses space/inset, stack or inline."
    },
    "16": {
      "$type": "dimension",
      "$value": "16px",
      "$description": "Raw space step, 16 px. Only for fixed distances inside a component; spacing that follows density uses space/inset, stack or inline."
    },
    "stack": {
      "xl": {
        "$type": "dimension",
        "$value": "{{space.24}}",
        "$description": "Gap between things stacked one above another, extra large."
      }
    },
    "inset": {
      "2xl": {
        "$type": "dimension",
        "$value": "{{space.24}}",
        "$description": "Padding inside a component, 2× large."
      },
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Padding inside a component, large."
      }
    },
    "inline": {
      "lg": {
        "$type": "dimension",
        "$value": "{{space.12}}",
        "$description": "Gap between things side by side, large."
      }
    }
  },
  "layer": {
    "modal": {
      "$type": "number",
      "$value": 1300
    }
  },
  "size": {
    "modal": {
      "sm": {
        "$type": "dimension",
        "$value": "400px"
      },
      "md": {
        "$type": "dimension",
        "$value": "560px"
      },
      "lg": {
        "$type": "dimension",
        "$value": "800px"
      }
    },
    "control": {
      "md": {
        "$type": "dimension",
        "$value": "{{size.40}}",
        "$description": "Height of a medium control, where controls insert by default: 40 comfortable, 32 compact. Dashboard recipe: use compact density; 40 for the page’s one main action and for forms; 32 for toolbars and filters, the size used most; 28 for actions inside table rows and cards. On phones switch to comfortable density, or give small controls an invisible 44 px tap area (size/target/comfortable)."
      }
    },
    "icon": {
      "lg": {
        "$type": "dimension",
        "$value": "{{size.24}}",
        "$description": "Icon box, large."
      }
    }
  },
  "border-width": {
    "hairline": {
      "$type": "dimension",
      "$value": "{{border-width.1}}",
      "$description": "Thin lines: dividers and almost every edge."
    }
  },
  "radius": {
    "container": {
      "lg": {
        "$type": "dimension",
        "$value": "{{radius.16}}",
        "$description": "Corners of cards, panels and dialogs, large."
      }
    }
  },
  "elevation": {
    "overlay": {
      "$type": "shadow",
      "$value": {
        "light": "0px 12px 16px 2px #373d431a, 0px 2px 8px 0px #373d4326",
        "dark": "0px 12px 16px 2px #00000026, 0px 2px 8px 0px #00000033"
      }
    }
  },
  "typography": {
    "heading-2xs-bold": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Funnel Display, Funnel Display Fallback, system-ui, sans-serif",
        "fontSize": {
          "min": "18px",
          "max": "24px"
        },
        "fontWeight": 700,
        "lineHeight": 1.2,
        "letterSpacing": "0px"
      },
      "$description": "Heading 2XS in bold: drawer and modal titles."
    },
    "body-sm": {
      "$type": "typography",
      "$value": {
        "fontFamily": "Geist, Geist Fallback, system-ui, sans-serif",
        "fontSize": "14px",
        "fontWeight": 400,
        "lineHeight": 1.5,
        "letterSpacing": "0px"
      }
    }
  }
}
```

## Purpose

Asks the reader to confirm before something that cannot be undone, such as deleting a project. It is a modal that stops the page, says what will happen, and offers two ways out: cancel, which harms nothing, and the action itself. Unlike the modal, it closes only on those two and Escape, never on a click beside it, so the question is never dismissed by accident.

## Anatomy

- The block, the modal's native dialog element over its scrim, with the alertdialog role. The modal's stylesheet draws it.
- The header part: an optional icon part and the title part, the question, on one line. There is no close button. While the confirm action is the danger one, the icon is drawn in the danger color.
- The body part: the description, what will happen, and anything more the question needs under it.
- A divider and the footer part, holding the cancel action and then the confirm action, at the end.

## When to use

- Before deleting, removing or overwriting something the reader cannot get back.
- Before an action that costs money or reaches other people and cannot be called back.

## When not to use

- A question with no lasting harm, or a small form: use a [modal](modal.md).
- Telling the reader something happened: use a [toast](toast.md).
- An action that can be undone: do it at once and offer Undo in a [toast](toast.md), rather than asking first.

## Behavior

Opens modally, with focus on the cancel action. Pressing cancel or Escape asks to close; pressing the confirm action calls `onConfirm` and then asks to close. A click on the scrim does nothing. A dialog that holds its own open state closes itself; one the page holds waits for the page to close it, through `onOpenChange(false)`. It fades in and rises into place over its scrim as the modal does, and under reduced motion fades in place. The size axis sets the greatest width, the modal's small or medium; the panel shrinks with the window.

The inline flag renders the panel in place with no scrim, so it can be seen in the Showroom and in documentation.

## Accessibility

### What it does for you

- It has the alertdialog role, so a screen reader announces it as an alert: its title names it and its description is read with it as it opens.
- Focus starts on the cancel action, the one that harms nothing, so pressing Enter by habit cancels. Focus is held inside while it is open and returns to the opener when it closes.
- A click beside it never closes it, so the reader must choose.
- The title is a second-level heading unless `headingLevel` gives another.

### What you must do

- Write the title as the question ("Delete this project?") and the description as what will happen, in a sentence or two.
- Name the confirm action after what it does ("Delete project"), never "OK" or "Yes".
- Give the cancel action's words in the page's own language when the page is not in English.

### How to test

- Press the control that opens it: focus is on the cancel action. Tab and Shift + Tab stay inside it.
- With a screen reader, it is announced as an alert dialog with its title and its description.
- Click on the scrim: it stays open. Press Escape: it closes, and focus returns to the opener.
- With text at 200% and the page 320 pixels wide, the panel shrinks with the window and both actions stay reachable.

### Keyboard

| Key | What it does |
|---|---|
| Tab or Shift + Tab | Moves focus between the actions and anything inside, and holds it there. |
| Enter or Space | Presses the focused action. Cancel has focus as it opens. |
| Escape | Asks to close, as cancel does. |

## Usage rules

### Do

- Ask only before what cannot be undone.
- Say in the description exactly what goes ("Website redesign and its 24 pages").

### Don't

- Ask before everyday actions: an alert dialog asked too often is confirmed without reading.
- Put a second destructive action in it.
