Banner
A full-width notice for an announcement, status change, or promotion. Banner
renders an <a-banner> with message, optional children, actions, and a dismiss
control. Content starts at the inline edge; pass align="center" to center it
(Align). closable defaults to true.
Banner is borderless. Set border-bottom-width for a rule or border-width for
an outline (Styling).
Playground
Tone
Pick a semantic tone to tint the surface and text together — and the ✕ adopts the
same tone (it also sets the border color, used if you opt into a border). The set
matches Expander / Tag: neutral (the default) plus brand, info, success,
warning, and critical. Color comes from the theme tokens, so every tone tracks
light and dark mode automatically.
<Banner tone="neutral" message="Neutral — a plain notice." /> {/* neutral is the default */}<Banner tone="brand" message="Brand — a product announcement." /><Banner tone="info" message="Info — a new release is available." /><Banner tone="success" message="Success — your changes were saved." /><Banner tone="warning" message="Warning — your trial ends in 3 days." /><Banner tone="critical" message="Critical — the last deploy failed." />tone also accepts any literal CSS color for a one-off tint — the hue is kept
while lightness and chroma are pinned to the named-tone curve (and re-tuned for
dark mode), the same mechanism as Expander / Tag:
<Banner tone="#ff1493" message="…" /><Banner tone="rebeccapurple" message="…" />Round
round rounds the corners for a standalone banner that floats inside a
container, rather than the edge-to-edge strip. It stays borderless (add
border-width for an outline). Bare round clamps to a stadium (half the height);
pass a number (px) or a CSS length string for a custom radius.
<Banner round tone="info" message="A rounded, standalone banner." /><Banner round={12} tone="success" message="A custom 12px radius." />Align
align places the content row. start (the default) aligns it to the inline edge
and reserves space for the dismiss control. center centers it in the bar. Wrapped
message text follows the selected alignment.
<Banner tone="info" message="Start-aligned content." /> {/* start is the default */}<Banner align="center" tone="info" message="Centered content." />The layout follows direction: rtl.
Message and content
message is the leading content: a string at the banner type scale or any node.
children sit between the message and actions, for example a chip, link, or
secondary line.
<Banner tone="info" message="Deployment complete."> <Text size="small" priority="tertiary">· 2 minutes ago</Text></Banner>Actions
Pass actions to render trailing controls — buttons, links — as a compact row
after the message and children. Keep them small; the banner is a single line.
Give each action the banner’s tone so the controls read as part of the strip.
<Banner tone="brand" message="Upgrade to Pro for unlimited runs." actions={<Button size="small" priority="secondary" tone="brand" label="Upgrade" />}/>Stack several actions by priority in one banner: a primary call to action, a
secondary alternative, and a low-emphasis tertiary choice, all sharing the
banner’s tone.
<Banner tone="info" message="A new version is ready to install." actions={ <> <Button size="small" priority="primary" tone="info" label="Update now" /> <Button size="small" priority="secondary" tone="info" label="Release notes" /> <Button size="small" priority="tertiary" tone="info" label="Later" /> </> }/>Dismiss
A banner is closable by default: a full-height ✕ on the right edge dismisses it.
It rests dimmed and brightens when you hover or focus it (the Input dim-actions
affordance). On dismiss the bar collapses its height and fades out, clipped to the
box (instant under prefers-reduced-motion).
Leave it uncontrolled and the banner hides itself when the ✕ is clicked (try it):
{/* uncontrolled — the ✕ self-dismisses */}<Banner tone="success" message="Dismiss me — I'll disappear." />Set closable={false} to drop the built-in ✕ — for a permanent banner, or when you
drive dismissal from your own control.
<Banner tone="warning" message="A permanent notice — no dismiss button." closable={false} />Dismiss from an action
Give any control inside the banner data-banner-dismiss and it dismisses the
banner on click, through the same state contract as the ✕ — for an action that
closes the banner as it runs (an “Undo”, a “Got it”). Try it:
<Banner tone="success" message="Draft saved." actions={<Button size="small" priority="secondary" tone="success" label="Undo" onClick={undo} data-banner-dismiss />}/>It’s matched by presence on the nearest banner, so it works on any activated
control — Button, a plain <a>, or a MenuItem inside a dropdown — and from the
keyboard, since Enter/Space activation is a real click. Put it on the terminal
control, not a dropdown trigger (which would dismiss the banner as the menu opens):
<Banner message="Export ready." actions={ <Menu trigger={<Button size="small" label="Download" iconTrailing="chevron-down" />}> <MenuItem label="As CSV" onClick={csv} data-banner-dismiss /> <MenuItem label="As JSON" onClick={json} data-banner-dismiss /> </Menu> }/>In controlled mode the click requests a dismiss like the ✕ does — drive the
result through dismissed (below).
Controlled vs uncontrolled
Dismissal follows Anta’s shared state contract. For uncontrolled use, pass
defaultDismissed; clicking ✕ then dismisses the banner. For controlled use,
pass dismissed and onDismiss. The banner follows dismissed and reports a
dismissal request when ✕ is clicked. Update dismissed to accept the request,
or leave it unchanged to reject it. The wrapper uses booleans, while the element
uses a state string (closed means dismissed):
<Banner> (JSX) | <a-banner> (element) | |
|---|---|---|
| Uncontrolled | defaultDismissed | default-state="open" | "closed" |
| Controlled | dismissed + onDismiss | state="open" | "closed" + statechange |
| Change signal | onDismiss(), fired on a dismiss request | statechange, a cancelable CustomEvent<{ next, prev }> of 'open' / 'closed' |
Controlling it is how you persist a dismissal (write it to storage) or re-show the
banner later. The element never self-applies in controlled mode: set dismissed to
true to accept the request, or do nothing to reject.
// controlled — persist the dismissal across reloadsconst [hidden, setHidden] = useState(() => localStorage.getItem('release-banner') === 'hidden')<Banner tone="info" message="A new release is available." dismissed={hidden} onDismiss={() => { setHidden(true) localStorage.setItem('release-banner', 'hidden') }}/>At the element level statechange is cancelable and fires before the banner
applies anything, so a vanilla <a-banner> listener can preventDefault() a
dismiss. The <Banner> wrapper’s onDismiss is notification-only — drive rejection
through dismissed instead.
Component props
| Prop | Type | Default | Description |
|---|---|---|---|
| message? | ReactNode | — | Leading message: a string at the banner type scale or any node. |
| actions? | ReactNode | — | Trailing controls (buttons, links) rendered as a compact row after the
message and children. |
| children? | ReactNode | — | Free content placed BETWEEN the message and the actions. |
| tone? | neutralbrandcriticalinfosuccesswarningstring | 'neutral' | Semantic tone, or any literal CSS color ('#ff1493', 'rebeccapurple') for
a one-off custom tone. Named tones re-point the surface, text, and border
color (used if you opt into a border); a custom color keeps its hue while
lightness/chroma are pinned. 'neutral' (the default) is the same as omitting it. |
| align? | startcenter | 'start' | Positions the content row. 'start' aligns it to the inline edge; 'center'
centers it. Wrapped message text follows the selected alignment. |
| round? | booleannumberstring | — | Rounded corners for a standalone, floating banner (border-radius: 999px,
which clamps to a stadium) instead of the edge-to-edge strip. Still borderless
by default. Pass a number (px) or a CSS length string for a custom radius. |
| closable? | boolean | true | Whether the trailing ✕ dismiss button is present. false removes it — drive
dismissal yourself (a controlled dismissed, or your own control) instead.
It removes the built-in ✕ without making the banner un-dismissible. |
| role? | string | 'status' | ARIA role for the strip host. 'status' (a polite live region, so the notice
reaches assistive tech) by default; pass 'alert' for an urgent notice that
should interrupt, or a landmark role. |
| dismissed? | boolean | — | Controlled dismissed state. When provided, the consumer owns visibility: the
banner follows this prop only, and clicking ✕ requests dismissal (reject by
not updating). Requires onDismiss — controlled mode never self-hides, so
without a handler to set dismissed the ✕ can't close the banner. Leave
undefined for uncontrolled. |
| defaultDismissed? | boolean | — | Initial dismissed state for the uncontrolled case (read once on mount). |
| onDismiss? | () => void | — | Fired when the user dismisses the banner (clicks ✕). Uncontrolled, the banner
hides itself and this notifies. Controlled, use it to accept the
request — set dismissed to true (or ignore to reject); pair it with
dismissed, or the banner can't be closed. |
Inherited props (className, id, slot, style, tabIndex, title)
| Prop | Type | Default | Description |
|---|---|---|---|
| className? | string | — | CSS class on the component's root element (merged with the component's own
classes). Use it directly for layout and positioning — grid/flex placement,
margins, alignment — rather than wrapping the component in a <div>/<span>. |
| id? | string | — | HTML id attribute. |
| slot? | string | — | Assigns the element to a named <slot> of a parent web component (e.g.
slot="header" inside a <Card>, slot="footer" inside a <Dialog>). |
| style? | CSSProperties | — | Inline styles on the component's root element. Set layout/positioning here
(or via className) directly on the component instead of adding a wrapper. |
| tabIndex? | number | — | Tab order. Set to -1 to skip the element when tabbing. |
| title? | string | — | HTML title attribute — native browser tooltip on hover. |
Plus every standard DOM event handler (onClick,
onFocus, onKeyDown, …) and any
data-* or aria-* attribute — forwarded as-is to
the underlying <a-*> element.
Web Component
Use the web component directly when you are not using React or Preact and a native control does not fit.
Slots hold the message, actions, and close control. A close button dispatches
dismissrequest through data-custom-event.
<a-banner role="status" tone="info"> <a-banner-message slot="message">A new version is ready to install.</a-banner-message> <a-button slot="actions" role="button" tabindex="0" size="small" priority="primary" tone="info"> <a-button-label>Update now</a-button-label> </a-button> <a-button slot="close" role="button" tabindex="0" priority="quaternary" tone="info" aria-label="Dismiss" data-custom-event="dismissrequest"><a-icon shape="x" aria-hidden="true"></a-icon></a-button></a-banner> Styling
Reach for the props first: tone sets the color (any CSS color for a custom
tone — it derives the surface, text, and border color in oklch), round the
corners. The one knob worth keeping is --banner-tone-source — the color a
custom tone derives from, so set it to drive the palette from your own variable.
Everything else is plain CSS on the host: min-height for a taller bar, a
border-*-width for a rule or outline (below).
<Banner tone="#e0457b" message="Custom" />Add a border. The banner is borderless by default, but its border style and
color are preset at 0 width (the Progress reset), so you opt in by setting only
the width — border-bottom-width for a rule under the strip, border-width for
a full outline. The color is already tone-aware (--banner-border), so a toned
banner’s border matches its tone with no extra work.
<Banner tone="info" message="A rule under the strip." style={{ borderBottomWidth: 1 }} /><Banner round tone="success" message="A full outline on a round banner." style={{ borderWidth: 1 }} />The host a-banner is the bar — its surface, text, border, and layout
are all plain CSS on the host (@layer anta, so an un-layered rule of yours wins
without !important). For the individual regions, <a-banner> exposes
::part(message) / ::part(content) / ::part(actions) / ::part(close).
<Banner tone="info" message="A taller bar." style={{ minHeight: 56 }} />