Designer view
Banner
A region-wide persistent informational surface — typically pinned to the top of a page or section — that surfaces system-level notices, promotional content, or status messages that should be visible across the user's session. Distinct from Alert (contextual, bound to a specific situation) by its persistence and scope, from Toast (transient, corner-anchored) by its persistence and full width, and from Modal (blocking) by its non-blocking contract. Used for cookie consent, maintenance notices, beta-feature callouts, system-degradation warnings, and full-bleed promotional CTAs.
When to use
Use
For region-wide or page-wide persistent informational surfaces — cookie consent, maintenance notices, beta-feature callouts, system-degradation warnings, full-bleed promotional CTAs. Banner is non-blocking, persistent (survives navigation when configured), and visible across the user's session.
Avoid
For contextual situation-specific notices bound to a specific page or form — that is `Alert`. For transient confirmations after async actions — that is `Toast`. For blocking decisions or destructive confirmations — that is `Modal[variant=alertdialog]`. For on-demand description — that is `Tooltip`.
Versus related
- alert
`Alert` is contextual and bound to a specific situation (form error, recent action confirmation); `Banner` is page-wide / region-wide and persists across the session. Alert appears in response to an event; Banner persists as ongoing system signal.
- toast
`Toast` is corner-anchored, ephemeral, and action-triggered; `Banner` is region-wide, persistent, and system-pushed. They occupy opposite ends of the notification-permanence spectrum.
- modal
`Modal[variant=alertdialog]` is blocking and demands explicit response; `Banner` is non-blocking and persistent. Banner that needs to block the page is a redesign signal (use Modal); Modal that needs to persist across navigation is also a redesign signal (use Banner plus an in-page Modal triggered from it).
Figma anatomy
| Slot | Figma type | Hint |
|---|---|---|
container | frame | Auto-layout horizontal frame at full inline-size; padding from container token |
icon | instance | Icon component instance; glyph swap bound to variant |
title | text | Heading text style; bound to a component property |
body | text | Body text style; bound to a component property |
actions | frame | Auto-layout horizontal frame containing button or link instances |
dismiss-button | instance | Icon button instance; visibility bound to dismissible property |
Token usage per slot
container- spacing
- padding
spacing.compact - gap
spacing.compact
- padding
- color
- background
color.surface.bg - border
color.border.subtle
- background
icon- color
- foreground
color.text.accent
- foreground
title- color
- foreground
color.text.primary
- foreground
- typography
- size
text.md - weight
weight.semibold
- size
body- color
- foreground
color.text.primary
- foreground
- typography
- size
text.sm - lineHeight
leading.normal
- size
actions- spacing
- gap
spacing.tight - padding
spacing.tight
- gap
dismiss-button- spacing
- padding
spacing.tight
- padding
- radius
- corner
radius.sm
- corner
- color
- foreground
color.text.muted - ring
color.border.focus
- foreground
Figma ↔ Code property map
| Figma | Type | Code | Notes |
|---|---|---|---|
Variant | Variant | variant | Maps info / promotional / warning / error. Drives visual treatment plus icon glyph plus (where applicable) accessible role. |
Layout | Variant | layout | horizontal / stacked. At and below `breakpoint.sm` the layout is forced to stacked regardless of authored value. |
Dismissible | Boolean | dismissible | Toggles the dismiss-button slot visibility plus the dismiss event wiring. |
Persistent | Boolean | persistent | Controls whether the dismissed state survives page reload. True for one-time announcements (cookie consent, beta feature); false for session-only banners (maintenance that resolves at session end). |
Has Icon | Boolean | hasIcon | — |
Has Title | Boolean | hasTitle | — |
Title | Text | title | — |
Body | Text | body | — |
Actions | Instance Swap | actions | Swap the actions slot — typically 1–3 Button or Link instances. |
Icon | Instance Swap | icon | Variant-default icon glyph; designers may swap for domain-specific icons (e.g. a beta-tag icon for beta-feature banners). |
Motion
| Transition | Duration token |
|---|---|
open | motion.duration.fast |
close | motion.duration.fast |
Responsive behaviour
| Breakpoint | Change |
|---|---|
breakpoint.sm | At and below, the canonical layout switches from horizontal (icon · content · actions) to stacked (icon and title in row 1, body in row 2, actions in row 3 below). The container retains full-bleed inline-size; the dismiss button moves to the inline-end of the title row. For very narrow viewports, body prose may also wrap to more lines; banners exceeding 4 lines should be redesigned as Alert or Modal. |
breakpoint.md | Above this width, the layout property is honoured as authored. `horizontal` shows icon · content · actions inline; `stacked` retains the row-stacked layout regardless of available width. |
Internationalisation
RTL · mirroring
Banner inline layout follows logical direction — inline-start to inline-end ordering of icon · content · actions reverses visual position in RTL. Dismiss button moves to the visual left in RTL (still inline-end logically). Action button order reverses logically: primary action stays on inline-end (visual right in LTR, visual left in RTL). Variant icon glyphs are direction-neutral. Stacked layout is direction-neutral by inline-axis.
Text expansion
Title and body grow with translation; banner container inline-size is determined by parent (full bleed at document scope, region width at section scope), so growth wraps naturally to additional lines. Long-text languages (German, Russian, Finnish) routinely expand banner height by 20-30%; layout `stacked` accommodates better than `horizontal` for long text. Action button labels follow Button's expansion rules; multi-action banners may need to wrap actions to a second row under heavy expansion.
Variants, properties, states
Variants
Structurally different versions of the component.
infopromotionalwarningerror Properties
The same component, parameterised.
| Property | Type |
|---|---|
dismissible | boolean |
persistent | boolean |
hasIcon | boolean |
hasTitle | boolean |
layout | horizontal | stacked |
States
Browser/user-driven (interactive) vs. app-driven (data).
| Kind | States |
|---|---|
interactive | hoverfocus-visible |
data | opendismissed |
State transitions
| From | To | Trigger |
|---|---|---|
open | dismissed | User clicks the dismiss button (when dismissible: true); or the consumer programmatically removes the banner; or the underlying system state resolves (e.g. maintenance window ends) and the consumer rehides the banner. |
Figma↔Code mismatches
- 01 Figma
A banner drawn full-screen, blocking the page
CodeA banner with `position: fixed` covering the viewport
ConsequenceDesigners may scale the banner to dominate the page for visual impact. Implementations following the Figma file ship a banner that visually blocks content, hides the navigation, and traps the user in a CTA they may not want to engage with. The pattern collapses to Modal but without Modal's a11y contract.
CorrectBanner is a region-wide horizontal strip, not a full-screen surface. If the surface needs to block the page, redesign as Modal (with proper focus trap and a11y contract). The canonical Banner caps height at one-to-three rows of content; tall banners are a redesign signal.
- 02 Figma
Cookie consent banner drawn with only an "Accept all" button
CodeCookie consent banner with Accept-only — no Decline or Customise
ConsequenceDesigners may treat cookie banners as marketing surfaces with single CTA. Implementations following the design fail GDPR / ePrivacy compliance — users must be able to decline with a single action equally prominent as Accept.
CorrectFor cookie consent banners specifically, the canonical action set requires Accept and Decline as visually equal. Customise / preferences may be a third option but never replaces Decline. Document the legal-compliance constraint explicitly; design review enforces it.
- 03 Figma
Banner sticky at top with no skip-link
CodeBanner pinned to viewport top with `position: sticky` consuming Tab focus
ConsequenceDesigners draw a sticky banner that visually persists across scroll. Developers ship it; keyboard users reach the banner first on every page-level Tab cycle (when banner contains tabbable content), and the visual sticky behaviour interacts with browser autoscroll-on-focus unpredictably.
CorrectSticky banners require a skip-to-content link as the first focusable element after the banner (or before, depending on focus order). The canonical document-level skip-link already covers banners pinned at document scope; banners scoped to a region need a region-level skip-link.
- 04 Figma
Multiple banners stacked at the page top
CodeMultiple banners rendered, each in its own region
ConsequenceDesigners stack banners (cookie + beta-feature + maintenance + promotional) for visual mocks. Developers ship the stack; the page header is now half-banner, the user is overwhelmed, and the most recently-added banner is buried beneath legacy ones.
CorrectCap the canonical banner stack at 1 visible banner at a time per scope (document-scope or region-scope). Beyond that, queue banners by priority — system-state banners (maintenance) outrank promotional banners; legal-compliance banners (cookie consent) outrank everything until acknowledged.
Common mistakes
#banner-as-modal
Banner sized to block the entire viewport
The banner is rendered at `100vh` or `position: fixed` with full-viewport coverage. Visually blocks the page; user cannot reach content beneath without dismissing or activating. The pattern claims Banner semantics (`role="region"`) while behaving like Modal.
Banner is a horizontal strip — top, bottom, or section- anchored — that does not visually block content beneath. If the surface genuinely needs to block, redesign as Modal with proper focus trap and `aria-modal`. Do not stretch Banner past one-to-three content rows.
#banner-cookie-accept-only
Cookie consent banner without Decline option
The banner offers only "Accept all" or similar single- affirmative CTA. Decline is hidden, requires multiple clicks, or is absent entirely. Fails GDPR / ePrivacy / similar regulations that require equal-prominence consent and rejection.
Cookie consent banners have Accept and Decline as visually equal actions, both reachable in one click. Customise / preferences may be a third action but does not substitute for Decline. Cookie-banner consent is a legal contract, not a marketing surface — review with legal counsel before shipping.
#banner-multiple-stacked
Multiple banners stacked at the page top
Page renders cookie banner, beta-feature banner, maintenance banner, and promotional banner all at once. Visual hierarchy collapses; users either tune all out or miss the most important one (legal-compliance buried under promotional).
Cap the canonical banner display at 1 per scope at a time. Queue or prioritise by category: legal-compliance > system- state > beta-feature > promotional. Render the highest- priority banner; show others only after the higher- priority ones are acknowledged or dismissed.
#banner-dismissed-state-not-persisted
Dismissed banner reappears on every page load
User dismisses the banner; on the next navigation it reappears. The dismiss action feels meaningless; users learn to ignore the dismiss button entirely.
Persist the dismissed state for `persistent: true` banners — typically via localStorage keyed by banner id and version. Re-show only when the banner content changes (version bump) or when the consumer explicitly resets the dismissed state. For `persistent: false` banners (session- only), document the in-memory persistence contract.
#banner-color-only-variant
Variant differentiated by colour alone
The banner's variant is communicated only through background or border colour. Colour-vision deficient users cannot distinguish info from warning; the icon glyph and the text both fail to name the variant.
Triple-layer variant: visual (background / border / icon), explicit prose (the title or body names the variant — "Maintenance:", "Beta:", "Important:"), and where applicable accessible role. Colour alone is never the only differentiator.
Accessibility hints
| Slot | Accessibility hint | |
|---|---|---|
container | Apply `role="region"` with an `aria-labelledby` reference to the title (or `aria-label` when no title is shown). For banners conveying urgent system state, `role="status"` (polite) is acceptable — `role="alert"` (assertive) is almost never appropriate for banner-scope content because the message is not action-required at the moment of appearance. Banner persists across navigation; do not re-announce on every route change. | |
icon | Decorative — `aria-hidden="true"`. Variant intent is communicated through visible text and (where applicable) through `role`. Never rely on the icon glyph alone. | |
title | Use a real heading element of an appropriate level relative to the surrounding document outline. The container's `aria-labelledby` references the title's id when present. | |
body | Plain prose. Inline links (e.g. "Read our cookie policy") are valid in body prose; complex actions belong in the actions slot for keyboard reachability. | |
actions | Buttons keep native semantics; links keep anchor semantics. Order primary first (visually inline-end on LTR; SR announces in DOM order). For cookie banners with legal-compliance requirements, "Decline" must be as prominent as "Accept" — visual weight equality is canonical. | |
dismiss-button | Provide an accessible name ("Dismiss banner" or "Close notification"). Activation removes the banner from the document. Persistence (re-appears on next visit vs permanently dismissed) is a `persistent` property, documented separately. |