Zetta v1.6

Components · Forms & selection

Range

A slider for a value or a span. A muted track fills Primary up to a primary thumb with a canvas ring; the dual-thumb mode selects a range with a primary band between the handles. Optional ticks, a live value, and a label round it out.

Preview

Volume60
Price range$20–$70

Build with the skill

No package to install — hand this to your AI to generate or migrate the component in your stack.

Prompt for your AI
Apply the Zetta "Range" component to this project using the zetta-design-md skill.

WHEN TO USE
- Migrating or fixing existing code (the common case): audit the current implementation against the `range` spec (variants: single, dual, ticks, disabled) and CORRECT every deviation — replace off-spec values, don't only add new ones.
- New component: build it to the `range` spec in this project's stack.

AUDIT & FIX (when the component already exists)
Compare it to the skill's spec and fix, don't just append: hardcoded colors/hex, off-scale radius, wrong fonts, dark/navy fills, shadows used in place of borders (or missing borders), brand lime used as a CTA or interaction (must be primary indigo), and any missing or wrong hover / focus / active / disabled / invalid state or missing 2px focus ring. Replace each with the correct Zetta token.

ALWAYS
- Pull the authoritative spec and tokens from the skill — never guess or hardcode values.
- Use this project's own component conventions and framework idioms.
- Honor the Zetta guardrails (brand = identity only, primary = interaction, shadows for overlays) and verify in Light, Dark, and Accessibility.

Anatomy & rules

Label                          42
╭────────────────────────────────╮
│ ──────────●────────────────── │
│ 0                           100│
╰────────────────────────────────╯
  ↑ range-fill   ↑ range-thumb

Parts:

  • range-label — form label above the track
  • range-track — full-width background bar
  • range-fill — primary-colored fill from min to current value
  • range-thumb — draggable circle indicator
  • range-value — live numeric readout (optional, right-aligned beside label)
  • range-helper — helper text below track
  • range-tick + range-tick-label — optional step markers below track
State Track fill Thumb Notes
Default {colors.primary} to current value {colors.surface-card} + border
Hover Unchanged Slightly larger scale Thumb scale 1.1
Focus Unchanged 2px solid {colors.border-focus} outline Keyboard only
Active (dragging) Unchanged {colors.primary} fill Mouse down on thumb
Disabled {colors.disabled-bg} fill {colors.disabled-bg} not-allowed

When to use

  • Selecting approximate values where an exact number is not critical: brightness, volume, opacity, zoom level, price range endpoint.
  • Values on a continuous scale where the user benefits from visual feedback of the relative position.

When not to use

  • Exact numeric entry — use Text Field with number input instead.
  • Selections from a small, discrete set (< 5 options) — use Radio.
  • Step values that are non-linear (e.g. 1, 5, 10, 50, 100) — use Select instead.

Do

  • Always show the current value when precision matters. Use showValue or display it beside the label.
  • Set meaningful min and max — avoid 0–100 as a default when the real domain is narrower.
  • Use step to constrain to meaningful increments. A price slider in $100 steps is better than $1 steps.

Don't

  • Don't use for ranges with more than ~200 steps — the thumb becomes too small to click precisely.
  • Don't use a slider for data entry that will be submitted in a form requiring an exact value.
  • Don't hide the value — users need to know what they've set.
  • Label: noun phrase describing what is being set. "Zoom level", "Maximum price", "Font size".
  • Value display: show the unit inline — "72px", "$450", "80%".
  • Tick labels: show min and max values only unless the range has 5 or fewer meaningful stops.

Keyboard

Key Behavior
Tab Move focus to the slider thumb
Left / Down Arrow Decrease value by one step
Right / Up Arrow Increase value by one step
Page Down Decrease value by a large step (10×)
Page Up Increase value by a large step (10×)
Home Set to minimum value
End Set to maximum value

Accessibility

  • ARIA role: role="slider" with aria-valuemin, aria-valuemax, aria-valuenow, and aria-valuetext (for units: "72 pixels", "$450").
  • Label association: connect via aria-labelledby pointing to the label element.
  • Disabled: aria-disabled="true" and disabled attribute.
  • Touch target: the thumb must be at minimum 44×44px via padding or hit area expansion — the visual 16px thumb size is below threshold.
  • Value announcement: use aria-valuetext to provide a human-readable value with units. Screen readers read aria-valuenow as a raw number without context.

Spec

Values and token references resolved straight from the Zetta spec.

range
width
100%
cursor
pointer
range-track
height
4px
borderRadius
{rounded.full}
backgroundColor
{colors.surface-muted}
overflow
hidden
range-fill
height
100%
backgroundColor
{colors.primary}
borderRadius
{rounded.full}
range-thumb
width
16px
height
16px
borderRadius
{rounded.full}
backgroundColor
{colors.primary}
border
2px solid {colors.canvas}
boxShadow
{elevation.shadow.raised}
cursor
pointer
range-thumb-hover
backgroundColor
{colors.primary-hover}
transform
scale(1.15)
range-thumb-active
transform
scale(1.1)
backgroundColor
{colors.primary-active}
range-focus
outline
2px solid {colors.border-focus}
outlineOffset
4px
range-disabled
cursor
not-allowed
opacity
0.5
range-disabled-track
backgroundColor
{colors.disabled-bg}
range-disabled-thumb
backgroundColor
{colors.disabled-border}
cursor
not-allowed
range-label
fontFamily
Geist
fontSize
13px
fontWeight
500
textColor
{colors.ink}
lineHeight
1.40
range-helper
fontFamily
Geist
fontSize
12px
fontWeight
400
textColor
{colors.muted}
marginTop
{spacing.xs}
range-value
fontFamily
Geist
fontSize
13px
fontWeight
500
textColor
{colors.ink}
minWidth
32px
textAlign
right
range-tick
width
2px
height
6px
backgroundColor
{colors.hairline}
borderRadius
{rounded.full}
position
absolute
bottom
-10px
transform
translateX(-50%)
range-tick-label
fontFamily
Geist
fontSize
11px
fontWeight
400
textColor
{colors.muted}
position
absolute
bottom
-24px
transform
translateX(-50%)
range-wrapper
display
flex
flexDirection
column
gap
{spacing.xs}
range-thumb-min
zIndex
2
range-thumb-max
zIndex
2
range-thumb-overlap
zIndex
3
range-fill-between
height
100%
backgroundColor
{colors.primary}
position
absolute
range-fill-outside
backgroundColor
{colors.surface-muted}
range-vertical
width
6px
height
200px
cursor
ns-resize
writing-mode
vertical-lr
range-vertical-track
width
4px
height
100%
range-vertical-fill
width
100%
bottom
0
backgroundColor
{colors.primary}

Known gaps

  • Custom thumb shape (non-circular) — not yet specified.
  • Slider value tooltip (floating label above thumb on drag) — not yet specified.