# Alert

> Package `@umriss-ui/core`, version 0.40.0. Demo page: <https://romanhaendler.github.io/umriss-ui/core/alert/>. Every page in one line: [llms.txt](https://romanhaendler.github.io/umriss-ui/core/llms.txt); every page in full: [llms-full.txt](https://romanhaendler.github.io/umriss-ui/core/llms-full.txt).

A message that stays in the page until its matter is settled (also called a banner or callout). Reach for it when the reader must notice something here: a failed import, an approval waiting, a form that did not save.

```ts
import { Alert } from "@umriss-ui/core";
```

Demo page: https://romanhaendler.github.io/umriss-ui/core/alert/

`warning` and `danger` interrupt a screen reader at once; `neutral`, `accent` and `success` are read out politely when the reader is free. Choose the tone by what the reader has to do, not by the colour you want.

Dismissing is reported through `onDismiss` and nothing more: whether and when the message comes back is your state.

## Examples

### A message

A title and a sentence or two, standing where the matter is – here above a sprint board.

```tsx
import { Alert } from "@umriss-ui/core";

export default function AMessage() {
  return (
    <Alert title="Sprint 14 ends on Friday">
      Items still in progress on Friday at 17:00 move to Sprint 15 with their remaining estimate.
    </Alert>
  );
}
```

### Tones

Pick the `tone` by what the reader has to do; `warning` and `danger` also interrupt a screen reader at once.

```tsx
import { Alert, Stack } from "@umriss-ui/core";

export default function Tones() {
  return (
    <Stack gap={3}>
      <Alert tone="neutral" title="Maintenance window tonight">
        Search is read-only from 23:00 to 23:30 while the index is rebuilt.
      </Alert>
      <Alert tone="accent" title="New: latency by region">
        Each service's page now splits its 95th percentile by region.
      </Alert>
      <Alert tone="success" title="Billing is back within its objective">
        The 95th percentile has stayed below 400 ms for the last hour.
      </Alert>
      <Alert tone="warning" title="Webhook deliveries are delayed">
        The oldest message in the queue is seven minutes old. Deliveries still go out.
      </Alert>
      <Alert tone="danger" title="Checkout is failing card payments">
        About 3 % of payments have timed out since 09:42. INC-1048 is open.
      </Alert>
    </Stack>
  );
}
```

### Offer the way forward

Put the next step in `actions`, under the text it belongs to, rather than somewhere else on the page.

```tsx
import { Alert, Button } from "@umriss-ui/core";

export default function WithActions() {
  return (
    <Alert
      tone="warning"
      title="Invoice INV-26-0318 is waiting for your approval"
      actions={
        <>
          <Button size="sm" variant="primary">
            Review the invoice
          </Button>
          <Button size="sm" variant="ghost">
            Pass it on
          </Button>
        </>
      }
    >
      It is due on 15 April and books € 1,965.84 to Facilities (CC-4400).
    </Alert>
  );
}
```

### Let the reader dismiss it

`onDismiss` adds a close button and reports the click; whether the message ever comes back is your state.

```tsx
import { useState } from "react";
import { Alert, Button } from "@umriss-ui/core";

export default function Dismissible() {
  const [open, setOpen] = useState(true);

  return open ? (
    <Alert tone="accent" title="Tours now show their delivery windows" onDismiss={() => setOpen(false)}>
      Each stop carries the window the customer booked; late arrivals are marked in the tour list.
    </Alert>
  ) : (
    <Button size="sm" onClick={() => setOpen(true)}>
      Show the message again
    </Button>
  );
}
```

### Sum up a form's errors

When a save fails, one `danger` alert above the form names every field that stops it and leads to the first.

```tsx
import { useState } from "react";
import { Alert, Button, FormField, Input, Stack } from "@umriss-ui/core";

export default function AboveAForm() {
  const [tried, setTried] = useState(false);
  const [owner, setOwner] = useState("");
  const [budget, setBudget] = useState("");
  const missing = [owner === "" ? "Owner" : null, budget === "" ? "Monthly budget" : null].filter((name) => name !== null);
  const showErrors = tried && missing.length > 0;

  return (
    <Stack gap={4} style={{ maxWidth: 440 }}>
      {showErrors && (
        <Alert
          tone="danger"
          title="The cost centre was not saved"
          actions={
            <Button size="sm" onClick={() => document.getElementById(owner === "" ? "cc-owner" : "cc-budget")?.focus()}>
              Go to the first field
            </Button>
          }
        >
          {missing.join(" and ")} {missing.length === 1 ? "is" : "are"} still empty.
        </Alert>
      )}
      <FormField label="Name">
        <Input defaultValue="Customer service" />
      </FormField>
      <FormField label="Owner" required fieldId="cc-owner" error={showErrors && owner === "" ? "Name the person responsible." : undefined}>
        <Input value={owner} onChange={(event) => setOwner(event.target.value)} />
      </FormField>
      <FormField label="Monthly budget (€)" required fieldId="cc-budget" error={showErrors && budget === "" ? "Enter an amount." : undefined}>
        <Input numeric value={budget} onChange={(event) => setBudget(event.target.value)} />
      </FormField>
      <Button variant="primary" onClick={() => setTried(true)} style={{ alignSelf: "flex-start" }}>
        Save the cost centre
      </Button>
    </Stack>
  );
}
```

## When to use something else

- Confirming something that has already happened, which may pass unnoticed → Toast
- A question the user must answer before an action goes ahead → ConfirmDialog
- A list or panel with nothing in it → EmptyState

## Keyboard

| Key | Action |
|---|---|
| `Tab` | Reaches the actions, then the dismiss cross. |
| `Enter / Space` | On the cross, calls `onDismiss`; the alert goes when you remove it. |

The keys of [Button](https://romanhaendler.github.io/umriss-ui/core/button/#keyboard-button) apply here.

## Accessibility

The role follows the tone: `warning` and `danger` are an `alert`, which interrupts a screen reader; `neutral`, `accent` and `success` are a polite `status`, read when the reader is free.

The title is a paragraph, not a heading, so an alert does not enter the page's outline. The dismiss cross is named "Close message", or by `dismissLabel`.

## API

### `AlertProps`

| Prop | Type | Default | Description |
|---|---|---|---|
| `tone` | [`AlertTone`](https://romanhaendler.github.io/umriss-ui/core/alert/#type-AlertTone)<br>`"neutral" \| "accent" \| "success" \| "warning" \| "danger"` | `"neutral"` | How urgent the message is. Only `warning` and `danger` interrupt the screen reader - the other two report politely.<br>Shown in: [Configurator](https://romanhaendler.github.io/umriss-ui/core/alert/#configurator-alert), [Tones](https://romanhaendler.github.io/umriss-ui/core/alert/#tones), [Offer the way forward](https://romanhaendler.github.io/umriss-ui/core/alert/#with-actions) and 5 more |
| `title` | `ReactNode` | — | Heading; without it the body text carries the message on its own.<br>Shown in: [A message](https://romanhaendler.github.io/umriss-ui/core/alert/#a-message), [Tones](https://romanhaendler.github.io/umriss-ui/core/alert/#tones), [Offer the way forward](https://romanhaendler.github.io/umriss-ui/core/alert/#with-actions) and 5 more |
| `actions` | `ReactNode` | — | Actions below the text, e.g. "Try again".<br>Shown in: [Offer the way forward](https://romanhaendler.github.io/umriss-ui/core/alert/#with-actions), [Sum up a form's errors](https://romanhaendler.github.io/umriss-ui/core/alert/#above-a-form), [A deployment (Stepper)](https://romanhaendler.github.io/umriss-ui/core/stepper/#a-deployment) |
| `dismissLabel` | `string` | the wording's "Close message" | Label of the dismiss cross. |
| `ref` | `Ref<HTMLDivElement>` | — | Receives the component's root element. From `RefProp`.<br>Shown in: [A row that holds still (Sizes)](https://romanhaendler.github.io/umriss-ui/core/sizes/#a-row-that-holds-still), [An anchored surface (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#anchored-surface), [Take the width of the trigger (Popover)](https://romanhaendler.github.io/umriss-ui/core/popover/#width-from-anchor) and 2 more |

#### Events

| Prop | Type | Default | Description |
|---|---|---|---|
| `onDismiss` | `() => void` | — | Shows a dismiss cross and calls this.<br>Shown in: [Let the reader dismiss it](https://romanhaendler.github.io/umriss-ui/core/alert/#dismissible) |

Also takes every attribute of `<div>` – without `title`.

### Types on this page

#### `AlertTone`

How urgent an alert is; it also decides whether a screen reader is
interrupted.

```ts
type AlertTone = "neutral" | "accent" | "success" | "warning" | "danger";
```

## Known limits

- No timer: an alert stays until you remove it. A message that goes by itself is a toast.
- No stack or queue of messages; place each alert where its matter is.

What umriss deliberately does not build, and why: [ADR-0032](https://romanhaendler.github.io/umriss-ui/what-umriss-ui-is-not/).
