Skip to content

Carousels — Hero Expressive extension

components specs/components/carousels-expressive.kmd

Material 3 Expressive extension to base Hero carousel (specs/components/ carousels.kmd). Adds shape morphing between selected/peek items (square ↔ pill ↔ squircle) and gesture inertia driven by spring physics (motion.kmd R9.1). Connected to shape-library.kmd morph contract.

When this spec applies

Primary triggers

All triggers

Specification body

Spec — Carousels Hero Expressive

Strict extension to carousels.kmd §Hero layout. Adds shape morph + spring-driven inertia. Base spec remains valid; Expressive opt-in per surface OR per preset.

Princípios

  1. Strict extension — base Hero layout from carousels.kmd unchanged.
  2. Shape morph items — selected item uses larger radius, peek items smaller.
  3. Spring inertia — gesture release follows spring decay, NOT linear.
  4. Width tokens — large item 64-90% viewport; peek 10-32%.

R1 — Anatomy (extension)

Base Hero per carousels.kmd. Expressive adds:

   ┌──┐ ┌──────────────────┐ ┌──┐
   │ ◐│ │      ◯           │ │◐ │
   │  │ │   ⬢⬡⬢            │ │  │   ← peek items (Cookie-7-ish) + selected (Pill-ish)
   │ ◐│ │      ◯           │ │◐ │
   └──┘ └──────────────────┘ └──┘

During swipe, shapes morph in real-time (corner radius interpolates).

R2 — Shape tokens

PositionDefault shape (per shape-library.kmd)Corner radius
Selected (center large)Pill or shape-corner-extra-large32dp+
Peek (sides)shape-corner-large16-24dp
Mid-transitioninterpolated linearvaries

During swipe gesture:

  • Position 0% (center): selected shape full radius.
  • Position 50% (mid-swipe): item halfway out, radius interpolates toward peek.
  • Position 100% (off-screen): peek shape OR fully gone.

R3 — Spring inertia

Release gesture (swipe-release):

  • Velocity captured; preserved into spring motion-spatial-default.
  • Spring decay: snap to nearest item center (large OR peek).
  • Bounce: subtle overshoot (max ±5dp) by default; configurable per preset.

Forbidden anti-pattern: linear decay OR friction-based (felt "slidey").

R4 — Width tokens

TokenRangeUse
hero-item-large64-90% viewportSelected center
hero-item-peek10-32% viewportSides
hero-gap8-16dpBetween items

Compact width-class: large 90% + peek 5% (almost only large). Medium/Expanded: large 64-70% + peek 18% each side.

R5 — Per-preset variation

PresetHero Expressive style
material3Subtle (radius delta 16dp; minimal bounce)
material_expressiveFull (radius delta 32dp; bounce ±5dp)
material2Disabled — falls back to baseline carousels
terminal_classicn/a (no carousels in terminal)
brutalistSharp corners only; no morph; no bounce (instant snap)
cyberpunk_neonDefault + glow on selected
minimalist_monoNo radius variation; only width difference

R6 — Surface bindings

SurfaceAPI
FlutterExtension of KoderCarousel (per carousels.kmd); KoderCarousel(variant: hero, expressive: true)
Web<koder-carousel variant="hero" expressive>
ComposeKoderCarousel(variant = Hero, expressive = true)
SwiftUIidem
CLI / TUIn/a

R7 — Acessibilidade

  • Items: role="listitem" per base spec.
  • Selected: aria-current="true".
  • Swipe alternative: arrow keys advance one item at a time.
  • Reduced-motion: no shape morph; no bounce; snap direct (instant transition).
  • Touch target: each item ≥ 48dp tap area.

R8 — Reduced-motion

  • Shape morph: disabled (static shapes per position).
  • Spring inertia: replaced by linear snap (no overshoot, no bounce).
  • Item swap: instant.

R9 — i18n

Inherits from carousels.kmd. No new keys.

T-suite

  • T1 Mount Hero Expressive: selected item visible center with Pill shape; peek items sides with shape-corner-large.
  • T2 Swipe gesture: radius interpolates continuously during drag.
  • T3 Release with velocity: spring decay; snaps to nearest center.
  • T4 Bounce: subtle overshoot visible (~5dp) before settle in material_expressive.
  • T5 Width tokens: Compact width-class → large 90%; Medium → 64% + 18% peeks.
  • T6 Reduced-motion: no morph + no bounce; instant snap.
  • T7 Keyboard: arrows advance one item.
  • T8 Selected aria-current: assistive tech announces correctly.
  • T9 Material2 preset: falls back to base carousels (no extension).

Requirements (testable)

The Hero Expressive carousel SHALL render the selected center item with an extra-large corner radius (Pill / shape-corner-extra-large, 32dp+) and the side peek items with a smaller radius (shape-corner-large, 16–24dp), and during a swipe gesture SHALL interpolate each item's corner radius continuously between its selected and peek values as its position changes.

Scenario: Mount shows selected and peek shapes

  • GIVEN a Hero carousel mounted with the Expressive variant enabled
  • WHEN the initial layout settles
  • THEN the center item renders with a Pill / shape-corner-extra-large radius of at least 32dp
  • AND the side peek items render with a shape-corner-large radius between 16dp and 24dp

Scenario: Radius interpolates during drag

  • GIVEN a mounted Hero Expressive carousel at rest
  • WHEN the user drags the selected item halfway toward the peek position
  • THEN the item's corner radius reports a value strictly between its selected radius and its peek radius
  • AND the radius updates continuously as the drag position changes

On gesture release the Hero Expressive carousel SHALL carry the captured swipe velocity into a spring decay (motion-spatial-default) that snaps to the nearest item center, and SHALL NOT use linear or friction-based decay; in the material_expressive preset the settle SHALL include a subtle overshoot bounce of at most ±5dp before resting.

Scenario: Release snaps to nearest center via spring

  • GIVEN a Hero Expressive carousel being dragged with non-zero velocity
  • WHEN the user releases the gesture
  • THEN the carousel follows a spring decay and settles with an item centered
  • AND no linear or friction-based decay curve is used

Scenario: Bounce overshoot in expressive preset

  • GIVEN a Hero Expressive carousel using the material_expressive preset
  • WHEN the user releases a swipe with velocity
  • THEN the settling item overshoots its resting center by at most 5dp before coming to rest

The Hero Expressive carousel SHALL size the selected item using the hero-item-large token (64–90% of viewport) and peek items using the hero-item-peek token (10–32% of viewport), resolving to large 90% with 5% peek in the Compact width-class and large 64–70% with 18% peeks in the Medium/Expanded width-classes.

Scenario: Compact width-class sizing

  • GIVEN a Hero Expressive carousel rendered in the Compact width-class
  • WHEN layout is measured
  • THEN the selected item occupies 90% of the viewport width
  • AND each peek item occupies about 5% of the viewport width

Scenario: Medium width-class sizing

  • GIVEN a Hero Expressive carousel rendered in the Medium width-class
  • WHEN layout is measured
  • THEN the selected item occupies between 64% and 70% of the viewport width
  • AND each side peek occupies about 18% of the viewport width

The Hero Expressive carousel SHALL vary its morph and inertia by preset: the material2 preset SHALL disable the extension and fall back to the baseline carousel, and the brutalist preset SHALL use sharp corners with no morph, no bounce, and an instant snap.

Scenario: Material2 falls back to baseline

  • GIVEN a Hero carousel configured with the material2 preset and Expressive requested
  • WHEN it mounts and the user swipes
  • THEN no shape morph or spring bounce occurs
  • AND the carousel behaves as the baseline Hero carousel from carousels.kmd

Scenario: Brutalist uses instant snap

  • GIVEN a Hero Expressive carousel using the brutalist preset
  • WHEN the user releases a swipe
  • THEN items keep sharp corners with no radius interpolation
  • AND the carousel snaps to the nearest item instantly with no overshoot bounce

The Hero Expressive carousel SHALL expose the selected item with aria-current="true", SHALL advance exactly one item per arrow-key press as a swipe alternative, SHALL provide a tap target of at least 48dp per item, and under reduced-motion SHALL disable shape morph and bounce and perform an instant direct snap instead of spring inertia.

Scenario: Keyboard advances one item

  • GIVEN a focused Hero Expressive carousel with the selected item marked aria-current="true"
  • WHEN the user presses the right arrow key once
  • THEN the carousel advances by exactly one item
  • AND the newly selected item now carries aria-current="true"

Scenario: Reduced-motion disables morph and bounce

  • GIVEN a Hero Expressive carousel with the reduced-motion preference enabled
  • WHEN the user swipes and releases
  • THEN no shape morph and no overshoot bounce occur
  • AND the item swap resolves as an instant direct snap

Scenario: Tap target minimum size

  • GIVEN a mounted Hero Expressive carousel
  • WHEN each item's tap area is measured
  • THEN every item exposes a tap target of at least 48dp

References