---
description: Unified Alert (toast/banner) for app notifications — Figma + drift prevention
globs: app/**/*.tsx, stories/modals/Alert.stories.js, tests/components/Alert.test.tsx
alwaysApply: false
---

# Alerts and notifications

## Source of truth

- **Figma:** [Community Rule System — Modal / Alert](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=6351-14646) (node **6351-14646**).
- **Code:** `app/components/modals/Alert` — default export `Alert` from `Alert.container.tsx` (Figma docstring on the container).

## When to use `Alert`

Use **`Alert`** for **app-level, section-level, and shell-level** success, warning, error, and neutral status messages that should read as a designed system surface (not body copy alone).

Do **not** recreate the same job with ad-hoc UI: bordered `<p>`, free-standing `role="alert"` blocks, or raw `text-[var(--color-border-default-utility-negative)]` paragraphs for product messaging.

## Props (lowercase in code; match Figma intent)

| Concern | Prop | Notes |
| --- | --- | --- |
| Layout | `type` | `toast` — bottom accent bar, top rounded corners; `banner` — full rounded block, inline or stacked. |
| Intent | `status` | `default` \| `positive` \| `warning` \| `danger`. |
| Density | `size` | `s` \| `m` (Figma S/M). Typography and padding are implemented inside `Alert.container.tsx` — do not fork spacing per call site. |
| Copy | `title`, `description?` | Required title; optional description when `hasBodyText` is true. |
| Icon | `hasLeadingIcon?` | Default `true`. |
| Body | `hasBodyText?` | Default `true`; set `false` for title-only. |
| Dismiss | `onClose?`, `hasTrailingIcon?` | Close control shows only when `onClose` is provided **and** `hasTrailingIcon` is not `false`. Omit `onClose` for non-dismissible messages. |

Valid enum slices for Storybook / guards: `ALERT_*_OPTIONS` in `lib/propNormalization.ts`.

## Choosing toast vs banner

- **`toast`** — transient edge / bottom emphasis (e.g. completed flow), strong bottom border accent.
- **`banner`** — rounded block; for **page / shell / modal** messaging, mount inside a **`fixed`** (or equivalent) overlay wrapper with `pointer-events-none` on the outer layer and `pointer-events-auto` on the alert so layout chrome does not reflow when the message appears (see `CreateFlowLayoutClient` `topBanners`, profile overlays, `LoginForm`, `PostLoginDraftTransfer`).

## Exemptions (do not force `Alert`)

1. **Single-field validation** under a control — keep `TextInput` / `TextArea` `error` and helper text (e.g. invalid email on the login form) unless design explicitly moves that line into `Alert`.
2. **Marketing layout** — `HeroBanner`, `ContentBanner` are not system alerts.
3. **Landmarks** — `role="banner"` on headers/nav is not the `Alert` “banner” type.
4. **A11y-only live regions** — e.g. tooltip / incrementer `aria-live` for widget state, not product notifications.

## Copy

All user-visible strings go through **`messages/`** and `useTranslation` / message modules per `localization.mdc`.
