skip to content
menu
components · banner

banner

A banner tells people something about the content of the page, such as titles that can't be checked, in one or two plain sentences and with a way forward. The technical part waits behind “details”.

warning: 105 titles can't be checked: the catalog holds them without an identity.

when to use

  • For a condition of the page's content that lasts until it is resolved or dismissed: “105 titles can't be checked: the catalog holds them without an identity.”
  • For an error from loading or from an action, with a way forward: “Couldn't load the list.” and “try again”.
  • Above the content it is about. Show one or two at a time at most: with more, none of them reads as important.

when to use something else

  • To confirm an action someone just took. For “8 episodes queued”, use a toast: it says so and leaves.
  • For one item. A field's error goes in the error text of its field, and a row's state in a status in the row.
  • In place of a list that has nothing to show. When the list is empty, or couldn't load and there is nothing older to show, use an empty state where the list would be. Keep the banner for content that is still on the screen.
  • To stop everything for a decision. Use a modal.
  • For help that is always true about one control. Put it in the hint of the control's field.

anatomy

  1. Banner. A square box, tinted and framed in shades of the state's colour, with a thicker bar in the colour itself down its left edge.
  2. Glyph. The state's small symbol, in its colour, beside the first line of text. Assistive technology skips it.
  3. Text. One or two sentences, in the body face. It takes the free width.
  4. Actions (optional). Small buttons after the text. When there isn't room beside it, they move under it.
  5. Details toggle (when there are details). A quiet text button, “details”, with a small arrow that turns down while the details are open.
  6. Details (optional). The technical part, under the text, in monospace, set off by a thin line on its left. Line breaks are kept, and long ids break anywhere.
  7. Dismiss (optional). A small square button with a cross, at the right edge.

variants and states

There are five states: neutral, information, success, warning and error. Information is the default. Each has its own colour and its own glyph.

A screen reader announces each state in its own way: politely, at its next pause, or at once, interrupting what it is reading.

state what a screen reader says use it for
Neutral The text alone, politely A plain fact about the content: what runs on its own, what is switched off
Information “information:” and the text, politely Under way, or worth knowing: the list is being refreshed, an index is rebuilding
Success “success:” and the text, politely A good condition that lasts, such as every source answered in the last hour. Or what a save changed on this page: “Saved. Times on this page now show in UTC.” A plain confirmation is a toast
Warning “warning:” and the text, politely It works, but someone should look: titles that can't be checked, sources over their limit, a refresh that failed while the last data still shows
Error “error:” and the text, at once Failed or blocked, and needs action: an action that failed, a list that couldn't load. Give it a way forward
The sweep only searches; grabbing on its own is off in settings.
information: Asking the catalog what the library holds. The list follows.
success: Every source answered in the last hour.
warning: 2 sources are over their daily query limit until midnight.

details

The details keep the technical part, such as ids, raw errors and HTTP codes, behind a disclosure, in monospace. They stay closed until someone opens them, and an announcement never reads them out.

dismiss

A banner can carry a dismiss button. Offer it for a message people can read and move on from, such as what a save changed, or an action that failed. Leave it off a condition that is still true, like a list that can't load.

The banner doesn't remember that it was dismissed: the screen does, and brings the banner back when its condition changes again.

success: Saved. Times on this page now show in UTC.

behaviour

  • The banner's controls take focus in this order: its actions, then the details toggle, then dismiss. The text takes no focus.
  • enter or space on the details toggle shows or hides the details.
  • esc does nothing. A banner is part of the page; it doesn't float above it, as a dialog does.
  • The banner announces its text when it appears or changes. Accessibility, below, says what that means for each state.

The full keyboard map is on the banner's React page.

accessibility

  • Only the text is read out. The text is a live region. An update reads the whole message, and never the actions, the details or dismiss.
  • Errors interrupt. An error is announced at once; every other state politely, when the screen reader has a pause. A banner that is on the page from the start and doesn't change can be a still note instead, which isn't announced at all: it is then read with the page, like any other text.
  • The state word. Every state but neutral reads its name before the text, from words that are hidden on screen: “warning: 105 titles can't be checked”. The word can be translated, given to neutral as well, or left out. A status names only warning and error.
  • Not by colour alone. The glyph is hidden from assistive technology, and each state has a glyph of its own shape, so the state never rests on colour alone.
  • Closed details are neither read out nor reachable with the keyboard.
  • Dismiss is named “dismiss”, and it is larger than the minimum target size. The details toggle and dismiss show the same focus outline as every control; interaction explains it.

Today:

  • A live region announces changes to text it already holds. So a banner that is announced politely, and appears together with its text, may go unannounced in some screen readers. A banner that interrupts, as an error does, is announced as it appears.
  • Dismissing removes the button that had focus, and focus falls back to the page. Move it yourself, to the content's heading or to the next control.
  • The details toggle and dismiss have the same names on every banner. With two banners on a page, name them so that people can tell them apart.

content

  • The text. One or two plain sentences: what is true, then what it means or what happens next. “105 titles can't be checked: the catalog holds them without an identity.” It is in the body face, the reader's language, so it is written in sentence case. No ids, codes or raw errors.
  • Numbers. Exact, with a thousands separator: “2,500”, not “2.5k”.
  • An error says what failed, and offers the way forward: “Couldn't load the list.” and “try again”. When the last data still shows, keep it, and make the banner a warning that says how old it is: “Couldn't refresh the list; it shows what was loaded 4m ago.”
  • Actions. One or two small quiet buttons, each a verb, in the lowercase monospace voice: “show them”, “try again”, “ask the catalog again”. Not the emphasized button: the view's one emphasized action is elsewhere.
  • Details. The raw part, as it came: “GET /api/missing: http 502 bad gateway”, “request id 7d1c0e4a”. Line breaks are kept.

do and don't

do
warning: 105 titles can't be checked: the catalog holds them without an identity.
The plain words first, and the raw part behind “details”.
don't
warning: 105 items: identity_id is null (catalog item 3f2a9c1e-5b7d-4c11-9e02-6a1d0b8f4c21)
The raw part as the message: screen readers read the codes aloud, and nobody learns what it means.
do
warning: 2 sources are over their daily query limit until midnight. Searches use the other 3.
A condition that lasts. It stays until it is resolved or dismissed.
don't
success: 8 episodes queued.
The confirmation of something just done is a toast. As a banner, it stays and pushes the list down.
do
warning: 105 titles can't be checked.
A small quiet action: the way forward, without competing with the view's emphasized action.
don't
warning: 105 titles can't be checked.
An emphasized button in a banner competes with the one emphasized action of the view.
  • Message handling: which kind of message goes where, from a banner or a toast to inline text, details or a modal.
  • Toast: the confirmation of an action that just finished.
  • Empty state: in place of a list that is empty or couldn't load.
  • Status: the state of one item, in its row.

specs

state in code announced
Neutral state="neutral" Politely (role="status"), with no state word
Information state="information" (the default) Politely, after “information:”
Success state="success" Politely, after “success:”
Warning state="warning" Politely, after “warning:”
Error state="error" At once (role="alert"), after “error:”

Each state sets three colours: --state-<state> for the bar on the left and the glyph, --state-<state>-border for the frame, the details rule and the dismiss button's frame on hover, and --state-<state>-bg for the fill. The banner carries data-state.

part spec
Banner A row: the glyph, then the text with its actions and details, then dismiss. Padding 12px, 16px on the left. A 3px rule on the left in --state-*, a 1px --state-*-border frame, a --state-*-bg fill, square corners. Inter 13px, line-height 1.5, --fg. The glyph and the text 12px apart.
Glyph The state's 14px glyph in --state-*, set 3px down so it sits on the first line. aria-hidden. icon: true (the default), false to hide it, or an element of your own.
Text children. It takes the free width, at least 24ch before the actions wrap under it; 16px from the actions beside it, 4px above them once they wrap.
Actions actions. 8px apart, wrapping under the text when there is no room. One or two buttons, size="sm" and variant="ghost", never variant="primary".
Details toggle Shown when details is set, named by detailsLabel (default “details”). A quiet text button: JetBrains Mono 12px, --fg-muted (--fg on hover), lowercase, 28px tall, padding 0 8px. A 10px caret that turns 90° to point down while the details are open, in 120ms ease, with no movement under reduced motion. aria-expanded and aria-controls.
Details details; defaultDetailsOpen opens them on the first render. 8px under the text. JetBrains Mono 12px, line-height 1.55, --fg-muted, padding 8px 12px, a 1px --state-*-border rule on the left. Line breaks kept (white-space: pre-wrap), long IDs broken anywhere (overflow-wrap: anywhere). hidden while closed, and outside the live region.
Dismiss Shown when onDismiss is set, named by dismissLabel (default “dismiss”). A 28px square at the right edge (above the 24px minimum target size), with a 12px × in --fg-muted; on hover, --fg and a --state-*-border frame. The banner doesn't hide itself: onDismiss is called, and you stop rendering it.
Focus The details toggle and dismiss: the 2px --focus-ring outline, 1px outside, on keyboard focus only.
setting in code what it does
Announcing live: 'polite', 'assertive' or 'off' Polite gives the text role="status", assertive role="alert", off no role at all: a still note, read with the page. The default is assertive for error and polite for the rest. The text is aria-atomic, so an update reads the whole message.
State word stateLabel Visually hidden text before the message: the state's name, for every state but neutral. A string replaces the word (and gives neutral one); false leaves it out.