Carousels
Horizontally scrolling stack of items, optimized for browsing a collection at a glance. Material parity (`/components/carousels`). Covers Hero, Multi-browse, and Contained layouts; snap behavior; swipe gestures; accessibility for sequential item navigation.
Spec — Carousels
Facet *isual*of Koder Design. Material parity: https://m3.material.io/components/carousels.
3 layout patterns
| Layout | Item count visible | Use |
|---|---|---|
| *ero* | 1 large + 1 preview edge | Featured / spotlight ("Featured project") |
| *ulti-browse* | 3-5 mixed sizes | Browse-heavy ("Recent files") |
| *ontained* | 2-4 equal sizes | Card-like grid in a strip |
Pick by intent: Hero for "look at THIS"; Multi-browse for "scan across"; Contained for "uniform set you can scroll through".
Anatomy (Hero layout)
←─ →─
┌──────────────────────────────────┐ ┌─────┐
│ │ │ │
│ Featured item │ │ next│
│ (full width minus │ │ peek│
│ 16 px edge for peek) │ │ │
│ │ │ │
└──────────────────────────────────┘ └─────┘
● ○ ○ ○ ○ ← page indicator- *tem radius* 12 px (matches card defaults)
- *tem gap* 8 px
- *dge peek* 16 px (shows ~24 px of next/previous item)
- *age indicator* dots, 8 px circles, 4 px gap
- *nap* item
edge to containeredge
R1 — Multi-browse layout
Mixed item widths in a single row. Material reference: large, medium, small repeating pattern.
| Slot | Width | Position |
|---|---|---|
| Large | 200 dp | Center / start |
| Medium | 140 dp | Trailing large |
| Small | 80 dp | At edges, indicates more |
Items resize as they scroll across the visible area: focused = large, adjacent = medium, edges = small. Animation per scroll position.
R2 — Contained layout
Fixed item width, all equal. Width inferred from container:
| Container width | Items visible | Item width |
|---|---|---|
| Compact (< 600 dp) | 1.2 | ~80% of container |
| Medium (600-839 dp) | 2.2 | ~45% |
| Expanded (≥ 840 dp) | 3.2 | ~30% |
The .2 partial visible signals scrollability.
R3 — Snap behavior
- *nap to item* scroll-end snaps to nearest item edge
- *elocity threshold* fast flick passes through multiple items;
slow drag snaps to nearest
- *isable snap* opt
out via prop when freescroll is preferred(rare; document why)
- Snap uses
motion.kmdemphasized-decelerate easing (~350 ms)
R4 — Page indicator
Dots below carousel show:
- Total item count (one dot per item, or grouped per "page" if > 10)
- Current focus position (filled dot)
| Item count | Indicator |
|---|---|
| ≤ 10 | One dot per item |
| 11-30 | Grouped per visible page (e.g., 5 pages of 3 items) |
| > 30 | "1 / 47" text indicator instead of dots |
Tappable dots: tap dot scrolls to that item (motion-medium, ~300 ms).
R5 — Navigation controls
| Surface | Controls |
|---|---|
| Mobile | Swipe + dots (no arrow buttons) |
| Tablet portrait | Swipe + dots |
| Desktop | Arrow buttons (◂ ▸) at edges + dots + keyboard |
Arrow buttons appear on hover (default) OR always visible (opt-in). Disabled-state arrow when at start / end (38% opacity).
R6 — Keyboard navigation
When carousel has focus:
- Arrow Left / Right: scroll to previous / next item (animated)
- Home / End: jump to first / last
- Tab: focus moves to focused item; Tab again moves OUT of carousel
to the next focusable element after the carousel
- Enter / Space on item: triggers item's action (open, etc.)
R7 — Item content
Items are normally cards or images. Each item must have:
- A defined aspect ratio (16:9 / 4:3 / 1:1 — pick one per carousel)
- A focusable target (whole item is the target by default)
- An accessible name (
aria-labelor visible heading) - Loading state: skeleton matching item shape until content loads
R8 — Accessibility
- Container:
role="region"+aria-roledescription="carousel"+aria-labeldescribing collection - Items:
role="group"+aria-roledescription="slide"+aria-label="3 of 7"(position + total) - Page indicator:
role="tablist"; each dotrole="tab"witharia-selected="true"on current - Live region: when item changes via auto-advance, announce new
item's name (polite live region)
- Auto
advance ALWAYS pauses on hover, focus, or `prefersreduced-motion`
R9 — Autoadvance (optin)
Off by default. When enabled:
- Interval: 5-8 seconds per item (configurable; never < 4 s)
- Pause on hover / focus / touch-down
- Resume only after user interaction settles
- Always pair with manual controls (dots + swipe)
- Disabled when
prefers-reduced-motion: reduce
Auto-advance is for promotional / passive carousels only. Don't auto-advance functional content (settings, lists).
R10 — Animation
- *tem scroll* 1:1 with finger drag; momentum on release
- *nap settle* motion
emphasizeddecelerate (~350 ms) - *rrow click* scroll by one item, motion-medium (~250 ms)
- *ulti-browse resize* items grow / shrink as they cross focus
position (continuous interpolation)
- Reduced motion: no resize animation, no auto-advance, instant snap
R11 — Per-preset variation
| Preset | Visual |
|---|---|
material3 |
12 px corners, edge peek, multi |
material2 |
4 px corners, uniform sizes, no peek by default |
ios_cupertino |
Page dots only (no arrows even on desktop), flush items |
gnome |
Adwaita carousel with side fade, integrated arrow buttons |
windows_11 |
Wide layout with arrow chevrons at sides, no dot indicator |
brutalist |
Sharp corners, no peek, hard snap (no smooth scroll) |
terminal_classic |
Sequential [1/7] indicator + Arrow Left/Right navigation only |
R12 — Forbidden patterns
- ❌ Carousels with > 30 items (use grid / list instead)
- ❌ Carousels as primary navigation (use tabs or nav components)
- ❌ Hiding the page indicator entirely when count > 1 (user can't
see "there's more")
- ❌ Auto-advance < 4 s (too fast to read)
- ❌ Auto-advance for forms or settings content
- ❌ Carousel inside a horizontally-scrolling parent (nested
horizontal scroll = unusable)
- ❌ Carousel without keyboard navigation
- ❌ Carousel without
prefers-reduced-motionhandling - ❌ Items with variable aspect ratios (jarring resize on scroll)
Cross-link
themes/motion.kmd— emphasized-decelerate easing for snapthemes/shape.kmd— item corner radiusthemes/color-roles.kmd— indicator dot colorcomponents/cards.kmd— item content patternapp-layout/window-size-classes.kmd— Contained items-visible rulesfoundations/elements.kmd— Container + Navigator families