Skip to content

Snackbars

components specs/components/snackbars.kmd

Transient feedback message at the bottom of the screen — confirms an action, optionally offers a single action (undo, retry). Material parity (`/components/snackbars`). Distinguished from banner (persistent) and dialog (modal).

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Snackbars

Facet Visual of Koder Design. Material parity: https://m3.material.io/components/snackbars.

Where they fit

ComponentModalityPersistenceAction count
SnackbarNon-modalTransient (4-10 s)0 or 1
BannerNon-modalPersistent until dismissed0-2
DialogModalUntil dismissedUnlimited

Use snackbar for low-priority, transient confirmation. Switch to banner for persistent attention or dialog for blocking interaction.

Anatomy (with action)

   ┌────────────────────────────────────────────────┐
   │  File moved to Trash.              UNDO        │
   └────────────────────────────────────────────────┘
        ↑                                ↑
       message                        action
  • Container width: min 344 dp / max 672 dp; on Compact width fills minus 16 px margin each side
  • Height: 48 px (single line) / 68 px (two lines) / auto for longer
  • Padding: 16 px horizontal, 14 px vertical
  • Corner radius: 4 px
  • Container bg: inverse-surface (high contrast — light bg in dark mode, dark bg in light mode)
  • Message: body-medium (14/20, weight 400), color inverse-on-surface
  • Action: text button, inverse-primary color, weight 600
  • Elevation: 3 dp

R1 — Placement

SurfacePosition
Compact (mobile)Bottom-centered, 16 px from bottom safe-area
Medium / ExpandedBottom-left, 16 px from edges
Large (desktop wide)Bottom-left (default); allow bottom-right via prop

Snackbar floats above app content but BELOW navigation bars (bottom app bar, navigation bar). Anchor offset must account for bottom nav height + safe-area inset.

R2 — Duration

Snackbar typeDefault durationNotes
Plain message (no action)4 s"File saved."
With action (e.g., "UNDO")6 sGives user time to act
Long / important10 sCap at 10 s — beyond, use banner
IndefiniteOff by defaultOnly when user must act (rare)

Pause timer on hover / focus (resume when they leave). An Indefinite snackbar has no timer at all and MUST show a close × so the user can dismiss it manually.

Reduced motion (prefers-reduced-motion: reduce) removes the slide animation only (see R6); the auto-dismiss timing is unchanged — the snackbar still auto-dismisses on its normal duration. Reduced motion is a vestibular preference (no motion), not a timing one, so it does not disable auto-dismiss and does not add a close ×. The close × affordance belongs to the Indefinite / error / two-line variants (R4), never to the reduced-motion state.

R3 — Actions

  • 0 actions: snackbar acts as confirmation; tap snackbar dismisses
  • 1 action: text button on right (UNDO, RETRY, VIEW); tap action triggers callback + dismisses
  • 2+ actions: NEVER. Switch to dialog or banner.

Action labels:

  • Single-word imperative when possible: UNDO, RETRY, VIEW, DISMISS
  • All caps OR Title Case — pick one style per app and stick to it
  • Color: inverse-primary (high contrast against inverse surface)

R4 — Two-line variant

When message is longer than fits on one line:

   ┌────────────────────────────────────────────────┐
   │  Sync failed. Check your network and retry.    │
   │                                  RETRY  ×      │
   └────────────────────────────────────────────────┘
  • Action moves to second line, right-aligned
  • Optional close × button (added for error / important snackbars)
  • Container grows to 68 px

Never exceed 2 lines. Truncate or escalate to banner / dialog.

R5 — Queue behavior

Multiple snackbars: queue them.

  • Current snackbar finishes its duration (or user dismisses) → next pops up
  • New snackbar with same content while a same-content one is showing: reset its timer (don't queue duplicate)
  • Maximum visible: ALWAYS 1 — never stack snackbars

Programmatic dismissal: API to dismiss current immediately (e.g., on route change to clear stale snackbars).

R6 — Animation

  • Enter: slide-in from bottom edge (motion-medium, ~250 ms emphasized)
  • Exit: slide-out down + fade (motion-fast, ~150 ms)
  • Action tap: snackbar fades while action runs
  • Reduced motion: instant in / out; no slide

R7 — Dismissal

TriggerResult
Timer elapsesAuto-dismiss
User taps snackbar (no action variant)Dismiss
User taps action buttonRun action + dismiss
User taps × (two-line variant)Dismiss (no action)
User swipes (touch)Dismiss (mobile gesture)
Esc key (keyboard focus on snackbar)Dismiss
ProgrammaticDismiss

R8 — Accessibility

  • Container: role="status" (info) OR role="alert" (error)
  • aria-live="polite" (status) / assertive (alert)
  • Action button: clear label ("Undo move to trash", not just "Undo" if context is unclear)
  • Close × button: aria-label="Dismiss"
  • Screen reader announces full snackbar content + action label on appearance
  • Don't auto-focus snackbar (would steal focus from user task)
  • Don't rely on color (action is also bold text + button hit target)

R9 — Tone

Snackbars are intentionally neutral in tone. Don't tonally style with error / warning / success backgrounds — that's banner territory. Snackbar bg is always inverse-surface (high-contrast neutral).

If you need to express tone (error, warning), use a banner or dialog.

R10 — Density

DensityHeight (single)Padding
Compact40 px12 px / 8 px
Default48 px16 px / 14 px
Comfortable56 px20 px / 18 px

R11 — Per-preset variation

PresetContainerAction color
material3inverse-surface 4 px radiusinverse-primary
material2Dark gray 2 px radiusAccent color
ios_cupertinoRounded toast with blur backdropSystem blue
gnomeAdwaita In-app notification, slide from topAccent
windows_11Mica notification toastAccent
brutalistSharp black bg, white textInverted action button
terminal_classicSingle-line [msg] <UNDO> at bottomUnderlined action

R12 — Forbidden patterns

  • ❌ More than 1 action button
  • ❌ Stacking snackbars (queue or replace, never stack)
  • ❌ Snackbar with > 2 lines of text
  • ❌ Snackbar > 10 s (use banner instead)
  • ❌ Snackbar < 4 s (too brief to read for slow / impaired readers)
  • ❌ Auto-focus / focus trap (would interrupt user)
  • ❌ Tonal bg color (use neutral inverse-surface)
  • ❌ Snackbar with form inputs / interactive widgets (snackbar is for confirmation, not input)
  • ❌ Snackbar that requires reading to use the app (use dialog if blocking)
  • ❌ Snackbar without safe-area bottom inset on iOS / Android gesture-nav
  • themes/elevation.kmd — 3 dp tonal + shadow recipe
  • themes/color-roles.kmdinverse-surface / inverse-on-surface / inverse-primary tokens
  • themes/motion.kmd — emphasized easing for enter
  • themes/typography.kmdbody-medium for message
  • app-layout/safe-area.kmd — bottom inset rule
  • components/banners.kmd — persistent sibling
  • components/dialogs.kmd — modal sibling
  • foundations/elements.kmd — Marker family

Requirements (testable)

Requirement: At most one action button {#req-snackbar-single-action}

A snackbar SHALL expose at most one action button; a request to render two or more actions SHALL be refused (escalate to a dialog or banner instead), and tapping the single action SHALL invoke its callback and dismiss the snackbar.

Scenario: Single action runs callback and dismisses

  • GIVEN a snackbar rendered with one action labelled "UNDO"
  • WHEN the user taps the "UNDO" action button
  • THEN the action callback fires exactly once
  • AND the snackbar dismisses

Scenario: Two actions are rejected

  • GIVEN a snackbar configured with two actions ("UNDO" and "RETRY")
  • WHEN the snackbar is requested to render
  • THEN at most one action button is rendered
  • AND the second action is not stacked (the request is refused, directing the caller to a dialog or banner)

Requirement: Duration bounds and timer pause {#req-snackbar-duration}

A snackbar's auto-dismiss duration SHALL be at least 4 s and at most 10 s (plain 4 s, with-action 6 s), and the auto-dismiss timer SHALL pause while the snackbar has hover or focus and resume when they leave.

Scenario: With-action snackbar auto-dismisses after its duration

  • GIVEN a snackbar with an action shown with the default 6 s duration and no hover or focus
  • WHEN 6 s of unpaused time elapse
  • THEN the snackbar auto-dismisses

Scenario: Hover pauses the timer

  • GIVEN a plain snackbar with a 4 s duration that has been visible for 3 s
  • WHEN the pointer hovers over the snackbar for a further 5 s
  • THEN the snackbar remains visible (the timer is paused, not elapsed)
  • AND it resumes counting only after the pointer leaves

Requirement: Reduced-motion removes animation only {#req-snackbar-reduced-motion}

Under prefers-reduced-motion: reduce a snackbar SHALL NOT play the slide animation and SHALL appear/disappear instantly, while its auto-dismiss timing SHALL remain unchanged — reduced motion is a vestibular preference, not a timing one, so it SHALL NOT disable auto-dismiss and SHALL NOT add a close ×. The close × affordance is governed by the variant (Indefinite / error / two-line), not by the reduced-motion preference.

Scenario: Reduced motion removes the slide but keeps auto-dismiss

  • GIVEN the environment reports prefers-reduced-motion: reduce
  • WHEN a plain snackbar with a 4 s duration is shown with no hover or focus
  • THEN it appears without a slide-in animation
  • AND it still auto-dismisses after its 4 s duration

Scenario: Indefinite snackbar shows a close affordance

  • GIVEN an indefinite snackbar (no auto-dismiss timer)
  • WHEN it is shown
  • THEN a close × button is present for manual dismissal
  • AND the affordance is present because the variant is indefinite, not because of any motion preference

Requirement: Never stack; queue or coalesce {#req-snackbar-queue}

At most one snackbar SHALL be visible at any time; additional snackbars SHALL be queued and shown only after the current one is dismissed, and a new snackbar whose content matches the one currently showing SHALL reset that snackbar's timer instead of enqueuing a duplicate.

Scenario: Second distinct snackbar waits in queue

  • GIVEN a snackbar A is currently visible
  • WHEN a second snackbar B with different content is requested
  • THEN exactly one snackbar (A) is visible
  • AND B is shown only after A is dismissed

Scenario: Duplicate content resets the timer

  • GIVEN a snackbar showing "File saved." that has been visible for 3 s
  • WHEN another "File saved." snackbar is requested
  • THEN no second snackbar is enqueued
  • AND the visible snackbar's auto-dismiss timer is reset to its full duration

Requirement: Dismissal triggers {#req-snackbar-dismissal}

A snackbar SHALL be dismissible by timer elapse, by tapping the action button (which also runs the action), by the close × on the two-line variant, by touch swipe, by the Esc key when it holds keyboard focus, and by programmatic call.

Scenario: Esc key dismisses a focused snackbar

  • GIVEN a snackbar that currently holds keyboard focus
  • WHEN the user presses the Esc key
  • THEN the snackbar dismisses

Scenario: Programmatic dismiss clears the current snackbar

  • GIVEN a snackbar is visible
  • WHEN the app calls the dismiss API (e.g., on route change)
  • THEN the current snackbar is removed immediately
  • AND no snackbar remains visible

Requirement: Accessibility semantics and focus {#req-snackbar-a11y}

A snackbar SHALL expose role="status" with aria-live="polite" for info and role="alert" with aria-live="assertive" for error, its close × SHALL carry aria-label="Dismiss", and it SHALL NOT auto-focus or trap focus so the user's current task is not interrupted.

Scenario: Error snackbar announces assertively

  • GIVEN an error snackbar is rendered
  • WHEN it appears
  • THEN its container exposes role="alert" with aria-live="assertive"
  • AND its content plus action label are available to the screen reader

Scenario: Appearance does not steal focus

  • GIVEN keyboard focus is on a text input in the user's task
  • WHEN a snackbar appears
  • THEN focus remains on the text input
  • AND the snackbar neither auto-focuses nor creates a focus trap

References