Banners

mandatory

Persistent informational bar at the top of a content region — carries a message and 1-2 actions. Material parity (`/components/banners`). Distinguished from snackbars (transient) and dialogs (modal); banners stay until dismissed and never block interaction.

Spec — Banners

Facet *isual*of Koder Design. Material parity: https://m3.material.io/components/banners.

Where they fit

Component Modality Persistence
*anner* Non-modal Persistent until user dismisses / acts
*nackbar* Non-modal Transient (4-10 s)
*ialog* Modal Persistent until user dismisses

Pick banner when: information requires action OR awareness over multiple page interactions, but does NOT need to block the user.

Anatomy

┌────────────────────────────────────────────────────────┐
│  ⓘ  Your subscription expires in 3 days.               │
│      Renew now to keep access.                         │
│                                  [Renew]   [Dismiss]   │
└────────────────────────────────────────────────────────┘
   ↑   ↑                              ↑         ↑
 icon  message                      action1   action2
  • *eight* 54 px (single line) / 90 px (two lines) / auto
  • *adding* 16 px vertical, 24 px horizontal
  • *con* 24 px, optional, leading
  • *essage* body-medium (14/20, weight 400)
  • *ctions* text buttons, trailing
  • *order* 1 px bottom outline-variant
  • *ackground* surface-container-low

R1 — Variants by tone

Tone Use Icon Background
*nfo* General announcement info (ⓘ) surface-container-low
*arning* Action needed soon warning (⚠) warning-container
*rror* Action needed now / degraded service error (✕) error-container
*uccess / promo* Positive announcement check (✓) success-container (extended)

Color alone never carries meaning — always pair tone with icon + text.

R2 — Action rules

  • * actions* dismissonly banner; tap × at end OR autoshow

    dismiss after read

  • * action* action + dismiss button (× icon at end)
  • * actions* primary + secondary text buttons; no separate ×

    (secondary serves as dismiss when labeled "Dismiss")

Maximum 2 visible actions. Don't put overflow menu in a banner.

R3 — Placement

Placement When
*op of page content*(default) One banner per page
*op of section* One banner scoped to a section
*nside list / form* Inline banner about a specific item

Never stack multiple banners. If multiple announcements pending, show the highest-priority one first; queue the rest after dismissal.

App bar sits ABOVE banner — banner anchors below the top app bar.

R4 — Persistence and state

  • Banner stays visible across scroll (does not stick to viewport;

    scrolls with content)

  • Dismissal is REMEMBERED per user + per banner ID — don't re-show

    the same dismissed banner unless conditions change

  • For "cookie consent" or "system upgrade" type banners, document

    re-show conditions explicitly

R5 — Animation

  • *ppear* slidedown from top edge (motionmedium, ~250 ms)
  • *ismiss* fadeout + slideup (motion-fast, ~150 ms)
  • Content below shifts smoothly when banner appears / disappears

    (no abrupt layout jump)

  • Reduced motion: instant show / hide

R6 — Accessibility

  • role="status" (info) or role="alert" (warning/error)
  • aria-live="polite" (status) / assertive (alert)
  • Action buttons have full labels (not "Click here")
  • Dismiss × button: aria-label="Dismiss banner"
  • Keyboard: Tab into action buttons → dismiss button; Esc dismisses

    (when allowed by banner type)

  • Icon is decorative if message conveys tone in words — aria-hidden

R7 — Mobile responsiveness

Width Behavior
≥ 600 dp Single row, actions trailing
< 600 dp Two rows: message on top, actions on second row (left-aligned)

Truncate to 2 lines max on Compact width; expand to fit when actions move to a separate row.

R8 — Density

Density Padding Height (single line)
Compact 12 px / 20 px 48 px
Default 16 px / 24 px 54 px
Comfortable 20 px / 28 px 64 px

R9 — Per-preset variation

Preset Visual
material3 Tonal background by tone, no border, 24 px radius corners on rounded variant
material2 Sharp corners, 1 px hairline border
ios_cupertino Topanchored, slight translucency, dismiss as swipeup gesture
gnome InfoBar style — flat tonal, 6 px border-radius
windows_11 Mica backdrop tint, accent color border-left
brutalist Solid color block, 4 px thick border, no rounded corners
terminal_classic Border-style box ┌── INFO ───┐ ... └──[OK]─┘

R10 — Forbidden patterns

  • ❌ Stacking multiple banners on one page
  • ❌ Banner over modal (banner is non-modal; modal already blocks)
  • ❌ Auto-dismissing banner with a timer (use snackbar instead)
  • ❌ More than 2 actions
  • ❌ Putting form inputs / interactive controls inside banner body
  • ❌ Showing the same dismissed banner without user-visible state change
  • ❌ Color-only tone signal (always pair with icon + words)
  • ❌ Sticking banner to viewport on scroll (annoying; use snackbar

    if must stay visible)

  • themes/color-roles.kmd — tonal container tokens
  • themes/elevation.kmd — banner sits at 0 dp (within surface)
  • components/snackbars.kmd — transient sibling
  • components/dialogs.kmd — modal sibling
  • foundations/elements.kmd — Container + Marker families

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/banners.kmd