Skip to content
Navigation

Notice

Persistent, inline messaging that stays on the surface — information, progress, confirmation, warning or error. Transient feedback is a toast; something the user must act on before continuing is a dialog.

Added 14 October 2026

Notice, Toast, or Dialog?

  • Stays until the state changes (a standing warning, a validation summary, a “not saved” explanation) → Notice. It occupies layout, so it can be re-read.
  • Already happened, needs no action (“Saved”) → toast. See docs/components/toast.md.
  • Blocks the user until answered → dialog. A notice never blocks.
  • Was named Alert before 4.2.0. The old name is a deprecated alias, removed in 6.0.0 — see CLAUDE.md → Renaming a public symbol.

Pick the tone from the state, not from how bad it feels

  • default — neutral, on a card surface. Use when the notice is the only thing on that surface and the tone would be noise.
  • info — context the user did not ask for but benefits from (“the plan saves automatically”).
  • success — a completed check or operation whose outcome the user is waiting on. Not for every save; a save that always works is a toast, or nothing.
  • warning — the current state is legal but will not do what the user probably wants (“no hour selected, so this cannot be saved”).
  • destructive — an operation failed, or data is unavailable. Something is broken now.
  • At most one destructive notice per surface. Two competing failures means the surface should say what is broken once, at the top.

Numbers in a notice are mono and tabular

  • Wrap any count, price or quantity in font-mono tabular-nums. The library’s mono contract (CLAUDE.md → Mono font) makes this non-optional for numeric values that are scanned, and notices in the planner are full of them (“412 sites”, “3 of 5 000 cd/m²”). The component cannot do this for you — it has no way to know which part of your sentence is a number.
  • Do NOT put a whole sentence in mono. Only the digits and their unit.
  • <code> is for identifiers and technical strings (a plan ID, an error reason), not for counts.

The tone is in the tint, the border and the icon — never in the text

  • Do not colour body text with the tone. On its own tint --destructive measures 2.90:1 and --success 2.55:1 in the light theme, both under the 4.5 minimum. The component paints title and body with the neutral foreground and puts the tone in the icon (--destructive-emphasis, --success-emphasis, --brand-interactive). Do not override that with className="text-destructive". [a11y]
  • Always give a toned notice an icon. Colour alone is not a state signal; the icon is the redundant channel a colourblind user reads. [a11y]
  • Reach for NoticeIcon / <hlm-notice-icon /> first. It draws the tone’s canonical glyph, so every warning in the product shows the same icon and nobody picks one per call site. The glyphs are the ones Toaster already ships — info Info, success CircleCheck, warning TriangleAlert, destructive OctagonX — so a toast and a notice about the same event look related.
  • It stays optional. Omit it for no icon at all; the leading column collapses. variant="default" has no canonical glyph and the slot renders nothing, which is why a neutral notice needs a hand-picked icon or none.
  • Pass your own glyph instead of NoticeIcon only when the tone does not say it — a megaphone for “declare your advertising”, a spinner for work in progress. Do not swap warning’s triangle for a different warning icon; that is the drift this slot exists to prevent.
  • The component colours and sizes your glyph — pass it bare. Do not add a background, padding, radius or a colour of your own; the tone owns the colour, and a box around the icon was tried and rejected (on an already-tinted surface it read as a darker square, not as structure).

role is the caller’s decision

The component sets no role. Pick one per notice: [a11y]

  • role="alert" — an error caused by something the user just did. Assertive: the screen reader interrupts.
  • role="status" — progress or a result that arrives on its own, and text that is rewritten in place as async work lands. Polite: it waits for a pause.
  • Nothing — copy that is simply present when the page renders. This is the common case.

Never use role="alert" for a notice whose text changes as results arrive: every change to an assertive region is re-announced and interrupts again.

Sizes

  • default — the notice is its own block: a page-level or section-level message.
  • sm — inside a dense container next to the control it talks about: a form section, a panel, a table toolbar. Scales padding, type, icon and the action slot; title and body follow automatically.
  • In sm an action button is size="xs", not size="sm" — anything taller breaks the notice’s height.

One-liners

  • Omit NoticeDescription and the notice becomes a single line — icon, title and action centre on one axis. There is no prop and no layout variant; the absence of a description IS the signal.
  • Use it for a standing status the user only needs to register (“Plan saves automatically”, “412 sites accept this”), and for the same message in a toolbar or table header where a two-row block would push the layout around.
  • Do not fake one by putting the whole message in the title when it needs explaining. If the user has to be told what to do about it, that is a description, and the notice should be two rows.
  • A title long enough to wrap still centres the icon against the wrapped block. If that reads badly, the title is too long for a one-liner.

Composition

  • NoticeTitle is the sentence the user must read; it is not a heading label. “Sites removed” is a title, “Warning” is not — the tone already says that.
  • NoticeDescription explains what to do about it. A notice with a title that needs no explanation should omit the description rather than restate itself.
  • NoticeAction is the top-right slot, for one dismissal or one remediation. More than one action means this is a dialog. Keep it variant="ghost" so it does not compete with the page’s primary action.
  • Rich content (lists, paragraphs, links) goes inside NoticeDescription, which styles links and spaces paragraphs already.