Alert
A banner that reports something the reader did not ask for. It owns no state: it never hides itself, never runs a timer, and the only JavaScript in the file is the branch that decides whether a screen reader should interrupt.
- Stable
- React 19
- Zero JS
- 4 variants
Code
<Alert color="info">A software update is available.</Alert><Alert color="error">The server refused the connection.</Alert><div class="wayra-alert" role="status" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-color="info"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">A software update is available.</div></div><div class="wayra-alert" role="alert" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-color="error" role="alert"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">The server refused the connection.</div></div>The colour is the meaning, and it is the only prop most alerts need. It defaults to
neutral rather than primary, because “primary” says nothing about a piece of feedback —
an alert with no status is just a notice.
Variants
Section titled “Variants”Code
<Alert color="info">soft</Alert><Alert variant="filled" color="info">filled</Alert><Alert variant="outline" color="info">outline</Alert><Alert variant="dash" color="info">dash</Alert><div class="wayra-alert" role="status" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-color="info"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">soft — the role washed over the surface</div></div><div class="wayra-alert" role="status" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-variant="filled" data-color="info"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">filled — the role as the background</div></div><div class="wayra-alert" role="status" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-variant="outline" data-color="info"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">outline — the role as the edge</div></div><div class="wayra-alert" role="status" data-variant="soft" data-color="neutral" data-orientation="horizontal" data-variant="dash" data-color="info"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">dash — the same edge, dashed</div></div>soft is the default, which is a deliberate break from Button and Badge: those default to
filled because they are small, and a small saturated shape reads as one object. An alert
spans the container, and a full-width saturated bar shouts. soft mixes 8% of the role into
base-100 — the same wash Button spends on its ghost hover — and leaves the text alone.
Icon, title and actions
Section titled “Icon, title and actions”Code
<Alertcolor="warning"icon={<BatteryIcon />}title="Battery service mode"actions={<Button variant="outline" color="warning" size="sm">Details</Button>}>Peak performance is limited until it is checked at an authorised centre.</Alert><div class="wayra-alert" role="status" data-variant="soft" data-color="warning" data-orientation="horizontal"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body"><div class="wayra-alert-title">Battery service mode</div><div class="wayra-alert-description">Peak performance is limited until it is checked at an authorised centre.</div></div><div class="wayra-alert-actions"><button type="button" class="wayra-button" data-variant="outline" data-color="warning" data-size="sm">Details</button></div></div>Three optional slots, each with a class of its own so the same markup can be written by
hand. Without a title the children are the whole body; with one they become the
description and drop a step in size. The library ships no icons — icon takes whatever you
hand it, and an <svg> inside is sized to 1.25em so it follows the reader’s text size.
Dismissing
Section titled “Dismissing”The alert has no onClose: visibility is the consumer's state.
Code
{visible && (<Alert color="error" actions={ <Button variant="ghost" color="error" size="sm" shape="circle" aria-label="Dismiss" onClick={() => setVisible(false)}> ✕ </Button> }> The server refused the connection.</Alert>)}<div class="wayra-alert" role="alert" data-variant="soft" data-color="error" data-orientation="horizontal"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body">The server refused the connection.</div><div class="wayra-alert-actions"><button type="button" class="wayra-button" data-variant="ghost" data-color="error" data-size="sm" data-shape="circle" aria-label="Dismiss">✕</button></div></div>There is no close button in the component and no onClose prop. A dismissable alert is a
<Button> in the actions slot plus a piece of state you already own — which keeps the
alert free of a second button implementation, and keeps you in charge of what “closed”
means (hidden, collapsed, remembered).
Orientation
Section titled “Orientation”Code
<Alertorientation="vertical"color="info"icon={<AccessoryIcon />}title="A new accessory was detected"actions={<> <Button variant="outline" color="info" size="sm">Later</Button> <Button color="info" size="sm">Set up</Button></>}>Set it up now, or later from Settings.</Alert><div class="wayra-alert" role="status" data-variant="soft" data-color="info" data-orientation="vertical"><span class="wayra-alert-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><circle cx="12" cy="12" r="9.2"/><path d="M12 11v6M12 7.6v.01"/></svg></span><div class="wayra-alert-body"><div class="wayra-alert-title">A new accessory was detected</div><div class="wayra-alert-description">Set it up now, or later from Settings.</div></div><div class="wayra-alert-actions"><button type="button" class="wayra-button" data-variant="outline" data-color="info" data-size="sm">Later</button><button type="button" class="wayra-button" data-color="info" data-size="sm">Set up</button></div></div>vertical puts every part on its own row. It is one flex-direction, not a second layout,
which is why it costs two declarations and cannot drift from the horizontal case.
Styling contract
Section titled “Styling contract”The class and the data-* attributes are the API. The stylesheet is written against them,
so hand-authored markup gets the same banner with no React anywhere — that is what every
html panel on this page is.
Code
<Alert color="success" title="Hand-written">No component, same paint.</Alert><div class="wayra-alert" role="status" data-variant="soft" data-color="success" data-orientation="horizontal"><div class="wayra-alert-body"> <div class="wayra-alert-title">Hand-written</div> <div class="wayra-alert-description">No component, same paint.</div></div></div>The part classes are equally public: .wayra-alert-icon, .wayra-alert-body,
.wayra-alert-title, .wayra-alert-description and .wayra-alert-actions. unstyled
drops all of them together with the root class and leaves the data-* attributes in place.
API reference
Section titled “API reference”AlertProps extends ComponentProps<"div"> minus color, role and title. The first
two are managed (see below); the third is the banner heading, which costs the native
tooltip attribute of the same name.
| Prop | Type | Default | Description |
|---|---|---|---|
variant |
"soft" | "filled" | "outline" | "dash" |
"soft" |
How the role is spent: wash, fill or edge. |
color |
WayraColor |
"neutral" |
Semantic role, and the meaning of the alert. |
orientation |
"horizontal" | "vertical" |
"horizontal" |
Whether the parts share a row or stack. |
icon |
ReactNode |
— | Leading graphic, drawn in the role colour. |
title |
ReactNode |
— | Heading; turns the children into the description. |
actions |
ReactNode |
— | Trailing controls, aligned to the far edge. |
live |
"assertive" | "polite" | "off" |
derived | How assistive technology announces it. |
unstyled |
boolean |
false |
Drops the classes, keeps the data-*. |
There is no size, no shape and no width: an alert fills the container it sits in and
has one step, like Card.
Accessibility
Section titled “Accessibility”- The announcement role is derived, not typed.
warninganderrorrenderrole="alert"and interrupt; every other colour rendersrole="status"and waits for a pause.liveoverrides that, andlive="off"writes no role at all — the right choice for a banner that is part of the page rather than news. roleis not a prop. It is computed fromlive, so the markup and the announcement policy cannot disagree.- Inserted is not the same as changed. Most screen readers announce a
role="alert"as soon as it is inserted — the APG alert pattern says so — which is why{error && <Alert color="error">…</Alert>}works. A polite alert is arole="status", and a status region is announced when its content changes, not when it appears: MDN’s advice is to establish the region first and update it after. So a polite message that must be heard keeps its Alert mounted and swaps the children, and the component keeps the same region node across that swap. The APG also warns that an alert already on the page when it loads is not announced, which is one more reason a banner that is part of the page takeslive="off". - The role colour never paints running text. Measured on the default theme: a title in
its own role over the
softwash reads 4.07–4.33:1, under the 4.5:1 the colour contract promises against a cleanbase-100. The role now paints the icon and the edge, and all text isbase-content. - Nothing here is focusable. Any control inside
actionsis; give an icon-only dismiss button anaria-label.