# Alert

**Maturity:** experimental

A blocking, centered dialog that interrupts to confirm an action, surface a short message, or collect one small input.

## Import

```tsx
import { Alert } from "@liquidify/react/alert"
```

## Props

- `actions`: `readonly AlertAction[]` (required). The action buttons. Exactly two short actions lay out side by side; otherwise stacked.
- `children`: `ReactNode`. Optional accessory content (e.g. a `<TextField>`) between the message and the actions.
- `className`: `string | undefined`. Optional additional CSS class merged into the glass surface class. The supported per-instance customisation seam: override any `--lq-*` custom property from this class's CSS — the token cascade the engine reads (ADR-0004). No raw inline style is involved, so this stays ADR-0003 clean.
- `defaultOpen`: `boolean | undefined`. Uncontrolled seed for the open state (default `false`).
- `dismissible`: `boolean | undefined`. Whether Escape / a scrim tap dismiss the alert (forwarded to Overlay). Defaults to `true`.
- `labelProps`: `LabelProps | undefined`. Per-instance typography override for the title, forwarded to the internal {@link Text} (ADR-0028). Spread **last** over the computed default (`title3`/`bold`/`primary` with a message, else `body`/`secondary`), so an explicit consumer value wins.
- `material`: `"frosted" | "regular" | "clear" | undefined`; default `enclosing `<Material default>`, else `"regular"``. Material of the glass surface — `frosted · regular · clear` (§06). Forwarded to the engine, which resolves the chain `prop ?? <Material default> ?? "regular"`, so a bare surface inherits the enclosing provider default.
- `message`: `string | undefined`. Optional secondary message; Modal wires it as the `aria-describedby` target.
- `messageProps`: `LabelProps | undefined`. Per-instance typography override for the message, forwarded to the internal {@link Text} (ADR-0028). Spread **last** over the `body`/`secondary` default.
- `onOpenChange`: `((open: boolean) => void) | undefined`. Fires with the next open state on every close path (action, Escape, scrim).
- `open`: `boolean | undefined`. Controlled open state. Passing it switches Alert to controlled mode.
- `perf`: `"low" | "high" | undefined`; default `derived from `<Backdrop type>`, else `"low"``. Perf budget of the glass surface (ADR-0006/0007). Forwarded to the engine as `data-glass-perf`; `"high"` opts this surface into the edge-lensing filter. When omitted, derives from the enclosing `<Backdrop type>` (§04).
- `ref`: `Ref<HTMLDivElement> | undefined`. Forwarded ref to the rendered element — the component's native root (or the merged child element on a component that opts into {@link SlottableProps}). Generic over `E` (charter `docs/api-conventions.md` §4 rule 1, default `HTMLElement`) so a component with a polymorphic root (Link's navigation / action mode union, Text's `as`) can instantiate the concrete element per variant instead of leaving every consumer with a widened `Ref<HTMLElement>`. A component that does not narrow simply inherits the `HTMLElement` default — source-compatible with every pre-existing non-generic `extends ControlProps`.
- `title`: `string` (required). The dialog title — required, bold; Modal wires it as the `aria-labelledby` target.

## SwiftUI mapping

- ``isPresented: Binding<Bool>`` → ``open` / `defaultOpen` / `onOpenChange`` (direct): presentation triad (ADR-0015), forwarded to `Overlay`
- ``titleKey: Text` (the title)` → ``title: string`` (direct): required; bold; the `aria-labelledby` target
- ``message: () -> Text`` → ``message?: string`` (direct): secondary; the `aria-describedby` target
- ``actions: () -> View` (Buttons)` → ``actions: AlertAction[]`` (direct): `{ id, label, role?, onPress?, disabled? }`; each action carries a stable, consumer-supplied `id` — rows key by `id` (never `role+label`), duplicate labels are supported, a duplicate `id` or an empty collection is a dev-time error; activation runs `onPress` then closes
- ``Button.id` (collection identity)` → ``AlertAction.id: string`` (direct): required; the React key + collection identity (§16.6)
- ``Button(role: .destructive)`` → ``role: "destructive"`` (direct): red label (`--lq-color-action-destructive`)
- ``Button(role: .cancel)`` → ``role: "cancel"`` (direct): no visual emphasis — rendered like a `default` pill (Button's `cancel` role is a semantic hook only); the Escape / scrim-dismiss default
- ``Button(…).disabled(_:)`` → ``disabled?: boolean`` (direct): Shape N — native `disabled` on `<Button>`, no `aria-disabled`; hard-suppressed (no `onPress`, no close)
- ``actions: { TextField(…) }`` → ``children`` (direct): accessory slot above the buttons (**children role: accessory**)
- ``.font(_:)` / `.foregroundStyle(_:)` on the title` → ``labelProps?: LabelProps`` (direct): shared `Pick<TextProps,…>`; spread **last** into the title `TextLabel`, so a consumer override wins over the computed `title3`/`bold`/`primary` (message present) vs `body`/`secondary` (title-only) default
- ``.font(_:)` / `.foregroundStyle(_:)` on the message` → ``messageProps?: LabelProps`` (direct): spread **last** into the message `TextLabel` over its `body`/`secondary` default
- `(presentation chrome)` → ``material?` / `perf?`` (direct): `PresentationalProps` — the card's glass surface; the base is tint-free (P0.B6 — no `tint` axis; actions take the role palette)
- `(no SwiftUI analogue)` → ``ref?: Ref<HTMLDivElement>`` (web-only): forwarded to the semantic root — the dialog card (Modal's glass element, the `<div>` carrying `role="dialog"`); attached while the alert is open, `null` while closed
