Sliders

mandatory

Single-handle or range-handle control for selecting a numeric value along a continuous or discrete scale. Material parity (`/components/sliders`). Covers continuous vs discrete, single vs range, value label, step marks, and accessibility.

Spec — Sliders

Facet *isual*of Koder Design. Material parity: https://m3.material.io/components/sliders.

Variant matrix

Continuous Discrete
*ingle* Smooth drag, any value Drag snaps to steps
*ange* Two handles, smooth Two handles, snap to steps

Pick:

  • *ontinuous*when any value within range is meaningful (volume,

    hue).

  • *iscrete*when only certain values matter (rating 15, Tshirt

    size).

  • *ange*when user picks an interval (price min-max, time of day

    range).

Anatomy (continuous single)

   Volume                                   75
   ────●────────────────────────────────── ↑
   ↑   ↑                                  value label
 track handle
  • *rack* 4 px tall (default), pillshaped, `surfacecontainer-highest`
  • *ctive track*(filled portion): primary color
  • *andle* 20 px circle, primary color, 4 px halo on focus / drag
  • *rack gap* 4 px on each side of handle (visible separation;

    Material 3 detail)

  • *alue label* chip-shaped overlay above handle on drag
  • *it target* 48 × 48 px around handle (extends beyond visual

    20 px)

R1 — Discrete with step marks

   Rating                                   ★ ★ ★ ★ ☆
   ──●──┊──┊──┊──┊──┊
       1  2  3  4  5
  • Step marks: 2 px circles on the track at each step position
  • Marks under filled portion: on-primary color (subtle)
  • Marks over unfilled portion: on-surface-variant
  • Snaptostep: handle releases to nearest step
  • Step intervals must be uniform OR documented (e.g., powersof2)

R2 — Range slider

Two handles + filled segment between them:

   Price                            $35 ────  $120
   ──────●━━━━━━━━━━━━━━●──────────
         ↑              ↑
        min            max
  • Both handles draggable
  • Min handle cannot pass max handle (and vice versa)
  • Optional minimum gap between handles (e.g., min range = 5 units)
  • Two value labels on drag

R3 — Value label

When visible Detail
Drag in progress Always
Handle focused (keyboard) Always
Handle hovered (mouse) Optional (configurable)
Idle Hidden (default); pinnedon optin

Label content:

  • Numeric value (default)
  • Formatted value (e.g., "$35", "75%", "2 hr 30 min")
  • Custom string via formatter function

Label shape:

  • 28 px tall, pill-shaped
  • primary bg, on-primary text
  • label-medium (12/16, weight 500)
  • Anchored 12 px above handle; tip pointing down

R4 — States

State Visual
Rest Default
Hovered (handle / track) Subtle scale of handle to 24 px
Focused 4 px focus ring outside handle
Dragging Handle scale to 24 px + value label visible
Disabled 38% opacity entire slider, no interaction

R5 — Tick / step mark variants

For step ≥ 10, marks become noisy. Options:

  • *how all marks*(default for ≤ 10 steps)
  • *how only labeled marks*(e.g., every 5th)
  • *o marks at all*(use ranges with labels above)

Document chosen variant in component instance.

R6 — Numeric label format (per locale)

Per i18n/contract.kmd:

  • Decimal separator: 1.5 (enUS) / 1,5 (ptBR)
  • Thousands separator: 1,000 / 1.000
  • Currency / units: locale-aware

R7 — Keyboard navigation

Key Action (single) Action (range)
Tab Focus into handle Focus first handle; Tab again moves to second
Arrow Left / Down Decrement by step Decrement focused handle
Arrow Right / Up Increment by step Increment focused handle
Page Down Decrement by 10× step Same
Page Up Increment by 10× step Same
Home Jump to minimum Min handle to absolute min OR max handle to current min
End Jump to maximum Symmetric

R8 — Accessibility

  • Container: role="group" + aria-label (range slider needs group

    because it has two handles)

  • Single handle: role="slider" + aria-valuenow /

    aria-valuemin / aria-valuemax + aria-label

  • Range handles: each handle has role="slider" with own valuenow /

    min / max; min handle's max = current max handle value (and vice versa)

  • aria-valuetext for formatted value (e.g., "75 percent")
  • Live region announces value change during drag (polite)
  • Don't rely on color alone for active vs inactive segments

R9 — Animation

  • *rag* 1:1 with input (no easing)
  • *eleasetostep (discrete)* snap with motionemphasizeddecelerate

    (~200 ms)

  • *alue label appear* scalein 0.95 → 1 + fade (motionfast)
  • *alue label disappear* fadeout (motionfast)
  • *andle scaleup on hover / focus* motionfast
  • Reduced motion: no scale animation, no spring; instant changes

R10 — Range thumb collision

When two handles meet (min approaches max):

  • Default: handles cannot pass each other; second handle blocks
  • Optional: handles can swap (min handle's value becomes greater than

    initial max → roles reassign)

Pick policy per instance and document. Default is "no pass".

R11 — Density

Density Track Handle
Compact 2 px 16 px
Default 4 px 20 px
Comfortable 6 px 24 px

Hit target stays 48 × 48 px regardless.

R12 — Per-preset variation

Preset Track Handle Label
material3 Pill, 4 px gap each side of handle 20 px circle Pill-chip above
material2 Plain bar Solid 18 px circle No label by default
ios_cupertino Thin track Larger 28 px handle with shadow Inline above
gnome GtkScale narrow track Small 14 px handle None
windows_11 Accent track Pill-shaped handle Tooltip above
brutalist Solid block Square handle, 2 px border Blackonwhite pill
terminal_classic [████░░░░░░] 40 ASCII n/a Numeric trailing

R13 — Forbidden patterns

  • ❌ Slider for values with NO meaningful intermediate (use radio /

    segmented buttons)

  • ❌ Slider without value indication (label or visible value

    somewhere)

  • ❌ Hit target < 48 × 48 px around handle
  • ❌ Range slider where one handle can hide behind the other
  • ❌ Drag that updates value only on release (laggy / unresponsive)
  • ❌ Step intervals so dense they look continuous (use a continuous

    slider with snaponrelease instead)

  • ❌ Slider for >100 steps (use input field / numeric stepper)
  • ❌ Color alone for active / inactive (must work in monochrome)
  • ❌ Slider that triggers irreversible action on drag (e.g., file

    delete, sale checkout) — use confirmation pattern

  • themes/color-roles.kmdprimary for active track + handle
  • themes/motion.kmd — emphasized-decelerate for snap
  • themes/typography.kmdlabel-medium for value
  • i18n/contract.kmd — locale-aware numeric format
  • interaction/states.kmd — hover / focused / dragging layers
  • foundations/elements.kmd — Control family

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/sliders.kmd