Snackbars
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).
Spec — Snackbars
Facet *isual*of Koder Design. Material parity: https://m3.material.io/components/snackbars.
Where they fit
| Component | Modality | Persistence | Action count |
|---|---|---|---|
| *nackbar* | Non-modal | Transient (4-10 s) | 0 or 1 |
| *anner* | Non-modal | Persistent until dismissed | 0-2 |
| *ialog* | Modal | Until dismissed | Unlimited |
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- *ontainer width* min 344 dp / max 672 dp; on Compact width
fills minus 16 px margin each side
- *eight* 48 px (single line) / 68 px (two lines) / auto for
longer
- *adding* 16 px horizontal, 14 px vertical
- *orner radius* 4 px
- *ontainer bg*
inverse-surface(high contrast — light bg indark mode, dark bg in light mode)
- *essage*
body-medium(14/20, weight 400), colorinverse-on-surface - *ction* text button,
inverse-primarycolor, weight 600 - *levation* 3 dp
R1 — Placement
| Surface | Position |
|---|---|
| Compact (mobile) | Bottom |
| Medium / Expanded | Bottom-left, 16 px from edges |
| Large (desktop wide) | Bottom |
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 type | Default duration | Notes |
|---|---|---|
| Plain message (no action) | 4 s | "File saved." |
| With action (e.g., "UNDO") | 6 s | Gives user time to act |
| Long / important | 10 s | Cap at 10 s — beyond, use banner |
| Indefinite | Off by default | Only when user must act (rare) |
Pause timer on hover / focus / prefers-reduced-motion: reduce (no auto-dismiss in that case — show close × instead).
Reduced motion: still auto-dismisses, but no slide animation.
R3 — Actions
- * actions* snackbar acts as confirmation; tap snackbar dismisses
- * action* text button on right (UNDO, RETRY, VIEW); tap action
triggers callback + dismisses
- *+ 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
- *nter* slide
in from bottom edge (motionmedium, ~250 msemphasized)
- *xit* slide
out down + fade (motionfast, ~150 ms) - *ction tap* snackbar fades while action runs
- *educed motion* instant in / out; no slide
R7 — Dismissal
| Trigger | Result |
|---|---|
| Timer elapses | Auto-dismiss |
| User taps snackbar (no action variant) | Dismiss |
| User taps action button | Run action + dismiss |
| User taps × (two-line variant) | Dismiss (no action) |
| User swipes (touch) | Dismiss (mobile gesture) |
| Esc key (keyboard focus on snackbar) | Dismiss |
| Programmatic | Dismiss |
R8 — Accessibility
- Container:
role="status"(info) ORrole="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
| Density | Height (single) | Padding |
|---|---|---|
| Compact | 40 px | 12 px / 8 px |
| Default | 48 px | 16 px / 14 px |
| Comfortable | 56 px | 20 px / 18 px |
R11 — Per-preset variation
| Preset | Container | Action color |
|---|---|---|
material3 |
inverse-surface 4 px radius |
inverse-primary |
material2 |
Dark gray 2 px radius | Accent color |
ios_cupertino |
Rounded toast with blur backdrop | System blue |
gnome |
Adwaita In-app notification, slide from top | Accent |
windows_11 |
Mica notification toast | Accent |
brutalist |
Sharp black bg, white text | Inverted action button |
terminal_classic |
Single-line [msg] <UNDO> at bottom |
Underlined 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
Cross-link
themes/elevation.kmd— 3 dp tonal + shadow recipethemes/color-roles.kmd—inverse-surface/inverse-on-surface/inverse-primarytokensthemes/motion.kmd— emphasized easing for enterthemes/typography.kmd—body-mediumfor messageapp-layout/safe-area.kmd— bottom inset rulecomponents/banners.kmd— persistent siblingcomponents/dialogs.kmd— modal siblingfoundations/elements.kmd— Marker family