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).
Quando esta spec se aplica
Triggers primários
- Add a snackbar
Todos os triggers
- Confirm a completed action with a brief message
- Offer 'Undo' immediately after a destructive action
- Surface a non-blocking error / status update
Corpo da especificação
Spec — Snackbars
Facet Visual of Koder Design. Material parity: https://m3.material.io/components/snackbars.
Where they fit
| Component | Modality | Persistence | Action count |
|---|---|---|---|
| Snackbar | Non-modal | Transient (4-10 s) | 0 or 1 |
| Banner | Non-modal | Persistent until dismissed | 0-2 |
| Dialog | 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
- 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), colorinverse-on-surface - Action: text button,
inverse-primarycolor, weight 600 - Elevation: 3 dp
R1 — Placement
| Surface | Position |
|---|---|
| Compact (mobile) | Bottom-centered, 16 px from bottom safe-area |
| Medium / Expanded | Bottom-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 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
- 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
| 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
Referências
specs/foundations/elements.kmdspecs/themes/color-roles.kmdspecs/themes/elevation.kmdspecs/themes/motion.kmdspecs/themes/typography.kmdspecs/components/banners.kmdspecs/components/dialogs.kmd