Slider
Slider is the continuous-range control: a recessed SurfaceCut track, a gradient thumb that takes the theme accent, and a floating value bubble that lifts above the thumb while the user drags or focuses. Reach for it when the user cares about the proportion more than the exact digit — volume, brightness, opacity, font size, zoom, mix levels. A real <input type="range"> lives underneath, so arrow keys, Home/End, screen readers, and form submission all work out of the box.
<Slider value={volume} onChange={setVolume} className="w-72" />
<Slider
value={bright}
onChange={setBright}
color="yellow"
size="md"
className="w-72"
/>
<Slider
value={opacity}
onChange={setOpacity}
min={0}
max={1}
step={0.01}
color="purple"
className="w-72"
/>Usage
import { Slider } from '@cladd-ui/react';
<Slider value={volume} onChange={setVolume} />;Slider works in both controlled and uncontrolled modes. Pass value + onChange for controlled, or defaultValue for uncontrolled. onChange fires on every step while the user drags or arrows through values — pair it with debounce when the consumer is expensive (a network call, a heavy re-render in a sibling pane).
Slider, NumberField, or NumberScrubber?
Reach for Slider when the value is continuous and the user benefits from seeing where they are in the range — a 30 / 100 brightness reads at a glance from the filled segment. Reach for NumberField when the value is discrete and the user mostly nudges in steps (quantity, tip percent, page count) — the + / − buttons own the writes. Reach for NumberScrubber when the value is arbitrary precision and the user wants both a draggable handle and a typeable input in the same compact slot — the inspector pattern from design tools (16px, 1.45, -0.4).
Examples
Sizes
size accepts sm (default) and md. Like its siblings Switch, Checkbox, and Radio, Slider ships only the two sizes that fit cleanly into a row of controls — a full 2xs → 2xl scale doesn't earn its keep on a track-and-thumb control. sm is the inline default; reach for md when the slider is the focal control of its row or the surrounding controls are md-sized.
<Slider size="md" value={a} onChange={setA} className="w-72" />
<Slider size="md" value={b} onChange={setB} className="w-72" />Colors
color accepts any of the eleven cladd accent tokens — the accent drives both the filled segment of the track and the thumb's gradient fill. When unset, the slider inherits the theme accent from CladdProvider, so a brand-coloured slider doesn't usually need a color prop. Pass one when the slider signals semantics (red for "destructive volume", yellow for brightness) or when it sits alongside a non-default control that needs to read as a single unit.
<Slider
color="brand"
value={value}
onChange={setValue}
className="w-72"
/>Min, max, step
min (default 0), max (default 100), and step (default 1) define the value space. The defaults are calibrated for percentage-shaped values; override all three for normalised ranges (0 → 1 with step={0.01} for opacity), wide ranges (25 → 400 with step={25} for zoom levels), or signed ranges (-2 → 8 for letter spacing). The thumb snaps to step on drag and on keyboard arrow presses.
<Slider value={pct} onChange={setPct} step={5} className="w-72" />
<Slider
value={opacity}
onChange={setOpacity}
min={0}
max={1}
step={0.01}
color="purple"
className="w-72"
/>
<Slider
value={zoom}
onChange={setZoom}
min={25}
max={400}
step={25}
color="cyan"
className="w-72"
/>Value display
The slider renders a floating value bubble above the thumb while the user drags or focuses — handy for confirming the exact landing value mid-gesture. For the resting state, pair the slider with an external readout: a SectionTitle label on the left, the formatted value on the right, the track between them. Format the value however the unit calls for — raw integer for volume, xx% for normalised opacity, xxpx for size.
<div className="flex w-72 flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="flex items-baseline justify-between">
<SectionTitle>Volume</SectionTitle>
<span className="text-cladd-fg-soft tabular-nums">{volume}</span>
</div>
<Slider value={volume} onChange={setVolume} color="brand" />
</div>
<div className="flex flex-col gap-2">
<div className="flex items-baseline justify-between">
<SectionTitle>Opacity</SectionTitle>
<span className="text-cladd-fg-soft tabular-nums">
{Math.round(opacity * 100)}%
</span>
</div>
<Slider
value={opacity}
onChange={setOpacity}
min={0}
max={1}
step={0.01}
color="purple"
/>
</div>
</div>Debounce
debounce (in ms, default 0) delays the onChange call until the user pauses. The internal value still updates immediately, so the thumb tracks the drag with no visual lag — only the parent's onChange callback is throttled. Useful when the consumer is expensive: a colour-picker that re-renders a canvas, a search that hits an API, a Monaco editor that has to relayout. Drag the second slider below to feel the pause — the readout only catches up when you let go.
<div className="flex w-72 flex-col gap-4">
<div className="flex flex-col gap-2">
<div className="flex items-baseline justify-between">
<SectionTitle>Immediate</SectionTitle>
<span className="text-cladd-fg-soft tabular-nums">{immediate}</span>
</div>
<Slider value={immediate} onChange={setImmediate} color="brand" />
</div>
<div className="flex flex-col gap-2">
<div className="flex items-baseline justify-between">
<SectionTitle>Debounced 400ms</SectionTitle>
<span className="text-cladd-fg-soft tabular-nums">{debounced}</span>
</div>
<Slider
defaultValue={debounced}
onChange={setDebounced}
debounce={400}
color="orange"
/>
</div>
</div>Disabled and read-only
disabled dims the whole slider to 50% and blocks all pointer and keyboard interaction — read as "not available right now" (the audio source is muted at the OS level, the layer is locked). readOnly is the subtler one: the slider keeps full opacity but stops responding to drags and keys — read as "live indicator, but you can't move it from here" (an enforced organisation policy, a value driven by a sibling control).
<div className="flex w-72 flex-col gap-2">
<SectionTitle>Disabled</SectionTitle>
<Slider defaultValue={30} disabled />
</div>
<div className="flex w-72 flex-col gap-2">
<SectionTitle>Read-only</SectionTitle>
<Slider defaultValue={70} readOnly color="red" />
</div>Inspector row
The canonical design-tool pattern: a Surface panel with a label column, a slider column, and a value column on a single grid. Each row sets its own min / max / step because the unit is different (px for size, unitless for line-height, em-fractions for letter-spacing). The shared accent colour ties the group together; the value column does the formatting. This is what cladd was designed for: dense, but not crowded.
<div className="flex w-72 flex-col gap-2">
<SectionTitle>Typography</SectionTitle>
<div className="grid grid-cols-[6rem_1fr_3rem] items-center gap-2">
<span className="text-cladd-fg-soft">Size</span>
<Slider
value={fontSize}
onChange={setFontSize}
min={8}
max={64}
color="brand"
/>
<span className="text-right text-cladd-fg-soft tabular-nums">
{fontSize}px
</span>
<span className="text-cladd-fg-soft">Line height</span>
<Slider
value={lineHeight}
onChange={setLineHeight}
min={1}
max={2}
step={0.05}
color="brand"
/>
<span className="text-right text-cladd-fg-soft tabular-nums">
{lineHeight.toFixed(2)}
</span>
<span className="text-cladd-fg-soft">Letter spacing</span>
<Slider
value={letter}
onChange={setLetter}
min={-2}
max={8}
step={0.1}
color="brand"
/>
<span className="text-right text-cladd-fg-soft tabular-nums">
{letter.toFixed(1)}
</span>
</div>
</div>Playground
size, color, and the state toggles compose freely. The sm / md pair covers most cases; reach for md when the slider is the focal control of its row.
<Slider
value={value}
onChange={setValue}
size="md"
color="brand"
disabled={false}
readOnly={false}
className="w-72"
/>API Reference
| Name: Type | Default | Description |
|---|---|---|
| className: string | — | Extra classes for the slider container. |
| color: Color | — | Accent color for the active track segment and thumb. Default: theme accent. |
| debounce: number | — | Debounce onChange calls in ms. Defaults to 0 (immediate). |
| defaultValue: number | 0 | Initial value (uncontrolled). Default 0.Ignored when value is provided. |
| disabled: boolean | — | Visually dim the slider and disable interaction. |
| input: boolean | — | Reserved - currently unused in the rendered output (the underlying <input type="range"> is always present). Kept for parity with other form components. |
| max: number | 100 | Default 100. |
| min: number | 0 | Default 0. |
| onChange: (value: number, event?: ChangeEvent<HTMLInputElement>) => void | — | Fires while the user drags or types a new value (subject to debounce). |
| readOnly: boolean | — | Block dragging without the disabled visual treatment. |
| size: SliderSize | 'sm' | Slider size token. Drives track and thumb dimensions. Default 'sm'. |
| step: number | 1 | Default 1. |
| value: number | — | Controlled value. When omitted, the component falls back to uncontrolled mode using defaultValue. |