<!-- Generated by scripts/build-agent-kit.ts for @aistrike-dev/ui@5.0.1. Do not edit. -->

# Alert vs Snackbar

<!-- use-when: Telling the user something happened: persistent, transient, or blocking. -->

Both tell the user something happened, and both are coloured by severity, so they get used
interchangeably. They are not interchangeable. The question that decides it is: **must the message
survive until the user deals with it?**

| Surface | Lifetime | Placement | The user must |
| --- | --- | --- | --- |
| `Alert` | Stays until the condition changes | Inline, next to what it describes | Notice it, and usually act |
| `Snackbar` | Auto-dismisses after a few seconds | Floating, corner of the viewport | Nothing — it is a receipt |
| `Dialog` | Stays until dismissed, blocking | Centred, blocks the page | Acknowledge before continuing |

A `Snackbar` is a **receipt**: proof something you asked for happened. An `Alert` is a **condition**: a
state of the page that is true right now. If the user looks away and misses it, a receipt is no loss and
a condition is a bug.

There is **no `Banner` component** in the design system. A page-level notice is an `Alert` placed at the
top of the content region.

## Decision flow

```
Does the user have to act on this, or acknowledge it, before continuing?
        │
        ├── Yes, and nothing else should happen until they do ──► Dialog
        └── No
                │
                ├── Would losing this message cost the user anything?
                │        │
                │        ├── Yes - it is a condition that is still true ──► Alert
                │        └── No - it just confirms what they did ─────────► Snackbar
                │
                └── Is there simply no data to show? ──────────────────────► EmptyState
```

## Alert

An inline message describing **a condition that is currently true**. It lives next to the thing it is
about and stays there until the condition changes.

**Reach for it when:**

- Form validation failed, or a field needs attention.
- An API call failed and the data on screen is stale or incomplete.
- A page-level notice: "Your trial ends in 3 days", "Scanning is paused".
- Explaining why a control is disabled or a result is partial.

```tsx
import { Alert, AlertTitle, Button } from '@aistrike-dev/ui';

<Alert severity="error" action={<Button variant="ghost" size="small" onClick={retry}>Retry</Button>}>
  <AlertTitle>Could not load findings</AlertTitle>
  The scanner is unreachable. Showing the last successful run.
</Alert>
```

**Practical limits:** place it next to what it describes — a validation error belongs under its field,
not in a corner of the screen. `severity` carries the meaning (`error`, `warning`, `info`, `success`);
never convey it with colour alone, so keep the text explicit. Give it an `action` when there is
something the user can do about it. Only add `onClose` when dismissing is genuinely safe — a dismissible
error the user cannot get back is worse than no error.

## Snackbar

A transient, auto-dismissing confirmation. Use it for **the receipt of an action the user just took**,
where missing it costs nothing.

**Reach for it when:**

- "Finding marked as resolved."
- "Settings saved."
- An undo prompt: "3 assets archived. Undo."
- A background job accepted: "Scan queued."

```tsx
import { Snackbar, Alert } from '@aistrike-dev/ui';

<Snackbar open={open} autoHideDuration={4000} onClose={close}>
  {/* An Alert child gives the toast role="alert", so it is announced. */}
  <Alert severity="success" onClose={close}>Finding marked as resolved.</Alert>
</Snackbar>
```

**Practical limits:** `autoHideDuration` is the whole point — if you find yourself setting it to `null`
so the message persists, you wanted an `Alert`. One snackbar at a time; queue them rather than stacking.
An undo `action` must stay available for the full duration. Never put an error the user has to fix in a
snackbar: it disappears, and they are left with a broken page and no explanation.

## Worked examples

| Scenario | Surface | Why |
| --- | --- | --- |
| "Email is not valid" under a field | `Alert` (or the field's `error` + `helperText`) | A condition that is still true |
| "Settings saved" | `Snackbar` | A receipt; losing it costs nothing |
| Findings table failed to load | `Alert severity="error"` with a Retry action | The page is still broken |
| "3 assets archived. Undo." | `Snackbar` with an `action` | Transient, reversible |
| "Your trial ends in 3 days" | `Alert severity="warning"` at the top of the page | Persistent notice, no Banner exists |
| "Delete 12 findings?" | `Dialog` | Must be resolved before anything else |
| Scan finished successfully | `Snackbar` | Confirmation of a completed action |
| Scan finished with 4 errors | `Alert severity="warning"` | The user needs to act on the errors |
| No findings match the filters | `EmptyState` | Nothing is wrong; there is just no data |
| Session expired, must log in again | `Dialog` | Blocks all further work |
| A field is disabled because of a plan limit | `Alert severity="info"` near the field | Explains a condition that persists |

## Anti-patterns

- **An error in a `Snackbar`.** It vanishes, leaving a broken page and no explanation. Errors are
  `Alert`s, or a `Dialog` if they block everything.
- **A `Snackbar` with `autoHideDuration={null}`.** That is an `Alert` in the wrong place.
- **A hand-rolled `Box` "banner".** There is no `Banner` component because a page-level notice is just
  an `Alert` at the top of the content region.
- **A `Dialog` for information the user does not need to act on.** Blocking the page to say "Saved" is
  hostile. Use a `Snackbar`.
- **Colour-only severity.** The text must say what happened; `severity` styles it, it does not explain
  it.
- **A dismissible `Alert` for an unresolved error.** Once closed, the user cannot get it back.
- **An `Alert` far from what it describes.** A validation message at the top of a long form makes the
  user hunt for the field.
- **Stacked snackbars.** Show one, queue the rest.
- **An `EmptyState` used to report a failure.** "No data" and "we could not fetch the data" are
  different messages; the second is an `Alert`.

## Accessibility

- `Alert` renders `role="alert"`, so it is announced when it appears. Place it near the related content
  so the announcement matches what the user then finds.
- `Snackbar` needs an `Alert` child to be announced — a bare `message` prop may not reach a screen
  reader. Always nest an `Alert`.
- Auto-dismissal is a real accessibility problem: anyone reading slowly, using a screen reader, or
  navigating by keyboard may miss it entirely. That is the reason a snackbar can only ever carry
  information that does not matter if missed.
- An undo `action` must be keyboard reachable for as long as the snackbar is visible.

## When you are unsure, ask

The ambiguous case is **a success that is also a warning** — "the scan completed, but four hosts were
unreachable". A receipt says the first part; a condition says the second. **If you cannot confidently
choose, stop and ask, naming the options and the one you lean toward.**

> "The scan finished with 4 unreachable hosts. I'm leaning toward an inline `Alert severity="warning"`
> that stays on the page, since the user probably needs to act on those hosts, rather than a `Snackbar`
> that disappears. Do you want it persistent?"

> "There's a red notice at the top of this page in the design. Since there's no Banner component I'd
> use an `Alert severity="error"` at the top of the content region. Should it be dismissible?"

A single clarifying question is far cheaper than shipping a control whose behaviour surprises the user.

## References

- **Molecules → Alert**, **Molecules → Snackbar** — stories for both, including severities and actions.
- **Organisms → Dialog** — for messages that must block.
- **Organisms → EmptyState** — for "there is no data", which is not feedback.
- [MUI Alert](https://mui.com/material-ui/react-alert/) · [MUI Snackbar](https://mui.com/material-ui/react-snackbar/)
