Alert

An inline, semantic message: a filled icon disc, a title, an optional description, and either a dismiss button or an action cluster on the right. Only the disc takes the variant colour — the card and the copy stay neutral by design, so a column of mixed alerts reads as one calm list rather than five banners competing for the same attention. The disc is decoration a screen reader never reaches, so the variant also emits one visually-hidden word — "Error", "Warning", "Success", "Information" — ahead of the title, which is what keeps severity alive when colour is not. Each one arrives on the panel spring, rising 8px through a short blur, because an alert has just happened rather than having always been there. One thing on this page is not what ships: every specimen below is rendered with role="note". The component defaults to role="alert", which is an assertive live region, and fourteen of those on one route would announce themselves all at once to a reader who had only opened the page.

tsx
import { Alert, AlertTitle, AlertDescription, Button } from "@pestoui/core"

Semantic variants

All five, title-only and stacked, so the discs can be compared directly. default and info share a bare i, success takes a check, warning a bare !, destructive a cross. On screen, severity is carried by a 20px circle and nothing else — no tinted card, no coloured border, no coloured copy — which is what lets five of them sit in one column without the page turning into a warning label. It cannot be carried by colour alone, though, so each variant also announces itself: the fourth one is read as "Warning: 82% of this month's run credits are used", and the word is in an sr-only span, so it moves nothing. default announces nothing at all — plain ink is the absence of a severity rather than one of the five.

Run 4820 is queued behind two others
Information:
Maintenance window on Sunday, 02:00–04:00 UTC
Success:
Inbox Triage Assistant is live in your workspace
Warning:
82% of this month's run credits are used
Error:
Run 4821 failed — the connected token has expired

Title only, or title and description

AlertDescription is optional, and adding one changes the geometry rather than just the height: the card flips from items-center to items-start, so the × sits level with the first line instead of floating beside the middle of a two-line block — measured on the specimen further down that has both, it is 13px from the card's top and 38.5px from its bottom. An action cluster is the exception and opts out with self-center, staying at 19.75px from each edge. What does not change is the disc. It sits in a box exactly one line of the title tall and centres itself in that, so it lands on the first line of the title in both forms — measured on the two specimens below, the gap between the disc's centre and the title's line-box centre is 0px in each. There is no longer a separate nudge for the description case; there used to be, and being a 2px step standing in for a 1.25px gap it left the two forms three-quarters of a pixel apart. Use the short one when the title already says the whole thing; a description that only restates it costs a line and adds nothing.

Information:
Runs are paused while maintenance is open
Information:
Runs are paused while maintenance is open
Anything triggered before 04:00 UTC is queued and starts as soon as the window closes. Nothing in the queue is dropped.

Dismiss and actions

onDismiss renders the × button at the trailing edge and fires when it is pressed; action takes a right-aligned cluster, normally one or two Buttons, and centres it against the card whether or not a description is present. The two are independent, so an alert can carry both — the third one does, and the actions sit inboard of the dismiss. Keep the cluster to sm buttons: an alert is a line of content, not a toolbar.

Warning:
Two agents paused themselves
Each one failed three runs in a row.
Success:
Weekly digest delivered
Error:
Lost the connection to example.com
Six queued runs are waiting on it.

A different glyph, or no disc at all

icon replaces what the variant would have put inside the disc while keeping the disc itself, so a Sparkles on the default variant still arrives in the ink-filled circle. Passing null removes the disc outright and the copy takes the full width — and with nothing left to colour, variant stops having any visible effect on that alert, which is the honest signal that a disc-less alert is a quiet notice rather than a severity. It is still announced as one: the severity word is keyed to the variant and not to the disc, so dropping the circle cannot quietly take the severity away from the readers who never had it in the first place. Make it quiet for everyone with severityLabel={null}.

Draft replies are now written for you
Turn it off per agent under Settings → Composing.
Information:
This workspace is read-only
Ask an owner for edit access to change an agent or start a run.

Rewording or silencing the announcement

These two are pixel-for-pixel ordinary alerts — the difference is only audible. severityLabel replaces the word the variant would have announced, which is where a translation goes, or a phrase more specific than "Error"; severityLabel={null} removes it, for the case where the title already carries the severity in its own words and hearing "Error: Card declined" is one word too many. Silencing is the exception, not the pattern: with it gone, a screen reader is back to a destructive alert and a success alert reading as the same sentence.

Payment failed:
Card ending 0187 was declined
Card declined — no run credits were charged

Props

PropTypeDefaultDescription
variant"default" | "info" | "success" | "warning" | "destructive""default"Colours the icon disc, picks its default glyph, and chooses the severity word read out before the title. It touches nothing else on the card; once icon={null} removes the disc it has no visible effect at all, but it is still announced.
iconReact.ReactNode—Overrides the glyph inside the disc, keeping the disc and its variant fill. Pass null to drop the disc entirely; leave it undefined to get the variant's own mark.
severityLabelstring | nullthe variant's own wordThe severity read before the title and shown to nobody: "Error", "Warning", "Success", "Information", and nothing at all for default. It lives in an sr-only span, so it costs no pixels. Pass a string to translate or reword it, or null to announce nothing.
onDismiss() => void—Renders the trailing × button — labelled "Dismiss" for assistive tech — and fires when it is pressed. The alert does not remove itself; the caller owns that state.
actionReact.ReactNode—A right-aligned cluster, usually one or two sm Buttons. Stays vertically centred on the card even when a description has pushed the rest of the content to the top.

Pesto UI · 117 documented components