Components · Status and feedback
CalloutBanner
Page- or section-level banner: trials, maintenance, billing, failed syncs and narrowed scopes.
Page banner with an action
import { Button, CalloutBanner } from '@rookcom/monarch-react';
<CalloutBanner
placement="page"
tone="accent"
title="Your trial ends in 3 days"
actions={
<Button size="sm" variant="primary">
Choose a plan
</Button>
}
onDismiss={() => {}}
>
Add a payment method to keep your workspace and its data.
</CalloutBanner>Usage examples
Tones
import { Button, CalloutBanner } from '@rookcom/monarch-react';
<CalloutBanner tone="info" landmark="region" title="Scheduled maintenance">
Sign-in is unavailable on Sunday from 02:00 to 03:00 UTC.
</CalloutBanner>
<CalloutBanner tone="warning" title="Payment method expires this month" />
<CalloutBanner
tone="danger"
title="Sync failed"
live="assertive"
actions={<Button size="sm">Retry</Button>}
>
The registry did not respond. Your changes are saved locally.
</CalloutBanner>
<CalloutBanner tone="success" title="Domain verified" dismissible />Scope notice
import { Button, CalloutBanner } from '@rookcom/monarch-react';
<CalloutBanner
variant="scope"
tone="neutral"
live="polite"
actionPlacement="inline"
actions={
<Button size="xs" variant="ghost" onClick={() => {}}>
Show all Spaces
</Button>
}
>
Showing tickets in <strong>Support</strong>.
</CalloutBanner>Purpose
A message about the account, the product or the whole view, shown above the content it affects. It can carry actions, be dismissed (the product decides whether that sticks) and be a landmark.
Use when
- Account or workspace state that affects everything: a trial ending, a failed payment, a plan limit.
- Planned or ongoing maintenance, or a degraded service.
- A section-level problem with a fix, such as a failed sync with Retry.
- The view is narrowed to a Space, team or filter and the user should be able to clear it (
variant="scope").
Avoid when
- Confirming an action that just happened: use Toast.
- A short note attached to one field, row or card: use InlineNotice.
- A message about one section with a title and a paragraph that never needs dismissing: Alert is enough.
- The user must decide before continuing: use Dialog.
Design principles
- One banner per placement: stack at most one
pagebanner above the app and onesectionbanner above a view. - Lead with the consequence ("Your trial ends in 3 days"), then what to do, then the action.
- Silent by default. Set
liveonly when the banner appears because of something that just happened. - Dismissal persistence is the product’s:
onDismissfires, Monarch stores nothing, anddefaultOpen={false}hides a banner the user already closed. - Tone carries meaning, never colour alone: every tone has its own icon, and the text says what happened.
API reference
import { CalloutBanner } from '@rookcom/monarch-react';| Export | Kind | Rendering | Root class |
|---|---|---|---|
CalloutBanner | component | client | .mn-callout-banner |
Forwards its ref to the root element (<div>, <section> or <aside> depending on landmark).
All native attributes pass through to the root, including aria-label for a landmark with no title.
Renders nothing while closed.
Generated from the TypeScript declarations of packages/react/src/components/feedback/callout-banner.
Properties
CalloutBanner
A page- or section-level message about the account, the product or the current view: trials, maintenance windows, billing problems, a failed sync, a narrowed scope. Optionally dismissible, with an action slot and a landmark role.
| Prop | Type | Description |
|---|---|---|
actionPlacement | "inline" | "end" | `end` puts actions on the trailing edge (below the text on narrow screens); `inline` puts them right after the text, like a sentence that ends in a link. Default `end`. |
actions | ReactNode | Buttons or links, for example "Upgrade" or "Retry". |
children | ReactNode | |
className | string | |
defaultOpen | boolean | Initial visibility when uncontrolled. Default true. |
dismissible | boolean | Shows a close button. Implied by `onDismiss`. |
dismissLabel | string | Accessible name of the close button. Default "Dismiss". |
icon | ReactNode | Replaces the tone icon. Pass `null` for none. |
id | string | |
landmark | "none" | "complementary" | "region" | Makes the banner a landmark, so screen-reader users can jump to it: `region` (`<section>`) or `complementary` (`<aside>`). Named by `title`, or by `aria-label` when there is no title. Default `none` (`<div>`). |
live | "polite" | "assertive" | Announce the banner's text when it appears: `polite` (role="status") or `assertive` (role="alert", only for errors that block progress). Leave unset for a banner that is present when the page loads. |
onDismiss | (() => void) | Called when the user dismisses the banner (and shows the close button). Store "don't show again" here: Monarch keeps nothing. |
open | boolean | Controlled visibility. Leave unset to let the banner hide itself on dismiss. |
placement | "section" | "page" | `page`: a full-width strip across the top of the app or page (trial, maintenance, billing), with no rounded corners. `section`: a rounded callout inside the content. Default `section`. |
title | ReactNode | Bold first line. |
tone | CalloutBannerTone | Colour and default icon. `accent` uses the product accent, for trials, upgrades and announcements. Default `info`. |
variant | "callout" | "scope" | `scope`: a compact, neutral one-line notice that the view is narrowed ("Showing tickets in Support"), usually with an action that clears it. Default `callout`. |
Variants
| Prop | Values | Description |
|---|---|---|
tone | 'neutral''info''accent''success''warning''danger' | Colours and default icon. accent follows the product accent, for trials and announcements. Default info. Exposed as data-tone. |
placement | 'section''page' | section: a rounded callout inside the content. page: a full-width strip with only a bottom border, for the top of the app. Default section. Exposed as data-placement. |
variant | 'callout''scope' | scope: a compact one-line "you are looking at a subset" notice with no icon by default. Default callout. Exposed as data-variant. |
actionPlacement | 'end''inline' | end puts actions on the trailing edge (below the text on narrow screens); inline right after the text. Default end. Exposed as data-action-placement. |
landmark | 'none''region''complementary' | Root element and landmark role: <div>, <section> (region) or <aside> (complementary). Default none. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Open | – | Rendered. The default. |
| Dismissed | – | Uncontrolled: hidden after the close button is pressed. Controlled: hidden when open becomes false. |
| Live | role="status" | role="alert" (on the text) | Announced when it appears; only when live is set. |
Interaction behaviour
- The close button appears when
dismissibleoronDismissis set; pressing it callsonDismissand, when uncontrolled, hides the banner. - With
open, the banner stays until the parent setsopen={false}, for example after saving the dismissal. - If focus was on the close button, it moves to the next focusable element after the banner (or the previous one), never to the page body.
- Actions are ordinary buttons or links passed in
actions.
Keyboard support
| Keys | Action |
|---|---|
| Tab | Moves through the actions, then the close button. |
| Enter / Space | Activates the focused action or dismisses the banner. |
The banner itself never takes focus when it appears.
Accessibility
- Role
- none by default; region (
<section>) or complementary (<aside>) withlandmark - Accessible name
- A landmark is named by
title(aria-labelledby) or byaria-label.
- The live role (
statusoralert) is on the text, not the root, so a landmark keeps its own role and the announcement is only the message. - The tone icon is
aria-hidden; the title and body carry the meaning. - The close button is an IconButton labelled
dismissLabel(default "Dismiss"). - Text and icon colours come from tokens with contrast guaranteed in every theme: title and body on the tinted background, icons in the tone’s strong colour.
Responsive behaviour
- Below 40rem, trailing actions wrap under the text, aligned with it.
- The title and body wrap; nothing is truncated. Works down to 320px.
placement="page"uses the layout gutter (--mn-layout-gutter) so it lines up with page content.
Theme support
- Each tone uses its intent subtle, strong and border tokens;
accentuses the product accent, so each Rook product gets its own colour. - Light, dark and high-contrast themes apply automatically.
- No animation, so reduced motion needs nothing.
Do and don't
Do
- Store the dismissal in
onDismissand passdefaultOpen={false}(oropen) next time. - Give a page banner one clear action; link to details rather than packing in paragraphs.
- Use
landmark="region"for banners people may want to return to, such as maintenance notices.
Don't
- Do not stack several page banners; show the most urgent one.
- Do not use
live="assertive"for promotions or reminders. - Do not make a billing or security problem dismissible if it still blocks the user.
Related components
- Alert: Section message with title and body that is never dismissed.
- InlineNotice: One line attached to a field, row or card.
- Toast: Transient confirmation of an action.
- Button: Actions and the close IconButton.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | Button |
Intent subtle, strong and border tokens; --mn-layout-gutter for page placement.
Testing coverage
- Renders title, body, decorative icon and actions with tone, placement and variant attributes; axe clean.
- Forwards its ref and native attributes.
- landmark="region" and "complementary" render named
<section>/<aside>landmarks; axe clean. - A landmark without a title is named by aria-label.
- The live role sits on the text inside a landmark (alert / status).
- Dismisses uncontrolled, calls onDismiss and moves focus to the next control, or the previous one.
- Controlled
openkeeps it until the parent closes it;defaultOpen={false}renders nothing. - Custom dismiss label; scope variant with inline action and no default icon; icon replace and removal.
- Renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery; Cross-browser axe: /components/callout-banner |
Tests: packages/react/src/components/feedback/callout-banner