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
Alertbefore 4.2.0. The old name is a deprecated alias, removed in 6.0.0 — seeCLAUDE.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
destructivenotice 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
--destructivemeasures 2.90:1 and--success2.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 withclassName="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 everywarningin the product shows the same icon and nobody picks one per call site. The glyphs are the onesToasteralready ships — infoInfo, successCircleCheck, warningTriangleAlert, destructiveOctagonX— 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
NoticeIcononly when the tone does not say it — a megaphone for “declare your advertising”, a spinner for work in progress. Do not swapwarning’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
sman action button issize="xs", notsize="sm"— anything taller breaks the notice’s height.
One-liners
- Omit
NoticeDescriptionand the notice becomes a single line — icon, title and action centre on one axis. There is no prop and nolayoutvariant; 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
NoticeTitleis 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.NoticeDescriptionexplains what to do about it. A notice with a title that needs no explanation should omit the description rather than restate itself.NoticeActionis the top-right slot, for one dismissal or one remediation. More than one action means this is a dialog. Keep itvariant="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.