Slider
Range input for a single value or a two-thumb range. Radix-powered with Yuno-token chrome: 6px track (bg-muted, rounded-full), primary-filled range, 16px circular thumb (1px border-primary, bg-background, shadow-sm). Hover and focus reveal a 4px muted ring around the thumb. Supports both horizontal and vertical orientation.
Anatomy
Four parts. The Root manages state + keyboard; the Track is the 6px rail; the Range is the filled portion between (or before) the thumbs; each Thumb is a 16px draggable circle.
- 1Slider (Root)
The container. Manages state (defaultValue / value + onValueChange), constraints (min / max / step), orientation ('horizontal' by default, 'vertical' for column layouts), and keyboard nav. flex items-center by default; flex-col + h-full when orientation='vertical'.
- 2SliderTrack
The rail: h-1.5 (6px) w-full bg-muted rounded-full overflow-hidden. Swaps to h-full w-1.5 in vertical mode via data-[orientation=vertical]. Never override to a taller/thicker rail — 6px is the Yuno spec.
- 3SliderRange
The filled portion, positioned absolute inside the Track. bg-primary. For a single-value slider it fills from left to the thumb; for range mode it fills between the two thumbs.
- 4SliderThumb
The draggable handle: size-4 (16px), rounded-full, 1px border-primary, bg-background, shadow-sm. cursor-grab idle, cursor-grabbing while dragging. Hover/focus reveal a 4px ring-ring/40. Radix renders one Thumb per value in the array (single value → 1 thumb, range → 2 thumbs).
Variants
Two orthogonal axes: single value vs range, and horizontal vs vertical. Set defaultValue as an array with one element for single-value or two for range. Set orientation on the Root to switch axes.
<Slider defaultValue={[42]} max={100} step={1} /><Slider defaultValue={[20, 80]} max={100} step={1} /><div className="h-40">
<Slider
orientation="vertical"
defaultValue={[60]}
max={100}
step={1}
/>
</div><div className="h-40">
<Slider
orientation="vertical"
defaultValue={[25, 75]}
max={100}
step={5}
/>
</div>States
Track: idle (bg-muted). Range: bg-primary — grows/shrinks with the value. Thumb: idle (border + shadow-sm, cursor-grab), hover (4px ring-ring/40), focus-visible (same 4px ring — keyboard driven), dragging (cursor-grabbing + range follows), disabled (opacity-50, pointer-events-none, cursor-not-allowed).
Recipes
Ready-to-copy compositions covering the most common Yuno usages of this atom.
Standard pattern for settings and prototypes: a Label + a live-updating value on the right. State lives in the parent; onValueChange writes back.
const [value, setValue] = React.useState([42]);
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label>Value</Label>
<span className="text-sm tabular-nums text-muted-foreground">
{value[0]}
</span>
</div>
<Slider value={value} onValueChange={setValue} max={100} step={1} />
</div>Two-thumb slider for range filters (price range, date window, amount bounds). Provide defaultValue as [min, max] with two elements — Radix renders two thumbs automatically. Show both endpoint labels.
const [range, setRange] = React.useState([20, 80]);
<div className="space-y-3">
<div className="flex items-center justify-between">
<Label>Range</Label>
<span className="text-sm tabular-nums text-muted-foreground">
{range[0]} – {range[1]}
</span>
</div>
<Slider value={range} onValueChange={setRange} max={100} step={5} />
<div className="flex justify-between text-xs text-muted-foreground">
<span>0</span>
<span>100</span>
</div>
</div>Set step to force the thumb to snap to fixed increments (5 for percentages, 10 for weights). Useful when the value doesn't need pixel precision.
<Slider defaultValue={[50]} max={100} step={10} />Set orientation='vertical' + a fixed height (h-40 h-48 etc.) on the parent. Common for volume controls, equalizer bands, or side-panel sliders. Radix flips arrow-key mapping to up/down automatically.
<div className="h-48">
<Slider
orientation="vertical"
defaultValue={[70]}
max={100}
step={1}
/>
</div>Import
One import from @/components/ui/slider.
import { Slider } from "@/components/ui/slider";Props
Slider forwards all Radix Root props. The most useful for prototypes:
| Prop | Type | Default | Description |
|---|---|---|---|
| defaultValue | number[] | — | Uncontrolled initial value. Single value → [42]; range → [20, 80]. Radix renders one thumb per array element. |
| value / onValueChange | number[] / (value: number[]) => void | — | Controlled pair. Use for live-updating labels or when the state needs to live in the parent. |
| min / max | number | 0 / 100 | Range bounds. max is exclusive of nothing — the thumb reaches it. |
| step | number | 1 | Increment the thumb snaps to. Match the granularity your UI displays — no reason to allow 42.7 if the label rounds to whole numbers. |
| orientation | "horizontal" | "vertical" | "horizontal" | Layout axis. Vertical requires an explicit container height (h-40, h-48) — Radix cannot infer it. Arrow-key mapping flips to up/down automatically. |
| disabled | boolean | false | Locks the slider. Opacity drops to 50, pointer-events disable, cursor becomes not-allowed. |
| inverted | boolean | false | Reverses the direction. Rare in prototypes — use only when the surface's mental model is genuinely inverted (e.g. progress-remaining instead of progress-complete). |
| className | string | — | Merged onto the Root. Use for container sizing (max-w-md, w-full). Do not override the track height or thumb size. |
When to use
- Filters over a continuous range (price, weight, threshold, amount bounds).
- Settings sliders (volume, brightness, playback speed).
- Any value where 'approximately X' is enough — precise entry is a separate control.
When not to use
- Precise numeric input (amounts to two decimals) — use Input type='number'.
- Discrete option sets (S / M / L) — use ToggleGroup or a Select.
- Read-only display of a value — use Progress or a plain badge, never a locked Slider.
Usage
- Show the current value inline (either the live number or an anchored label) so the user reads without guessing.
- Set a sensible step so the thumb snaps to meaningful increments — nothing worse than a slider stopping at 42.7183 when the surface only shows whole numbers.
- For range mode, always show both endpoint values.
- For vertical, give the container an explicit height (h-40, h-48) — Radix can't infer it.
- Keep the track height at 6px and the thumb at 16px — the Yuno spec is deliberate.
- Don't tint the track or the range — bg-muted and bg-primary are the only allowed values.
- Don't inflate the thumb past size-4 (16px) — bigger thumbs feel toy-like at Dashboard density.
- Don't hide the min/max endpoints when the range is arbitrary (0-100 with no context).
- Don't wire Slider to a read-only value — use Progress.
- Don't stack two sliders vertically without labels — the second one gets ignored.
Related
Cross-links to atoms and patterns you may reach for next.
- InputReach for Input type='number' when the user needs pixel-precision (typing 42.37) — Slider is for approximate/coarse selection.
- ProgressProgress reports a value; Slider lets the user set one. Never wire a Slider read-only to display a value — use Progress.
- SwitchFor binary on/off — Slider is overkill.
- Toggle groupFor discrete option sets (S / M / L) — Slider is for continuous ranges.