Slider
A thumb on a track for one number, or two thumbs for a range.
cennge-ui deploys between 09:00 and 18:00, runs 4 jobs at a time and fails a build under 80% coverage.
- Drag a thumb, or click a label under a track: the nearest thumb moves there.
- Arrows step by one; Page Up, Page Down or Shift with an arrow step by ten; Home and End go to the ends.
- Parallel jobs stops only on its marks: the arrows go 1, 2, 4, 8, 16.
Field marks
- Import
import { Slider } from "cennge-ui";- Size
- 3.0 KB gzip for this import, its dependencies aside
- Pattern
- WAI-ARIA slider, one per thumb (Radix Slider)
- Keyboard
- Arrow left Arrow right Arrow up Arrow down PageUpPageDownHomeEnd
- State
- Controlled with
valueandonValueChange, a number or an array for a range; or uncontrolled withdefaultValue - SSR
- Rendered on the server in the Next.js check; hydrates without a warning
- Range
- Each thumb is named after the label plus “Minimum” or “Maximum”;
thumbLabelschanges those words - Commit
onValueCommitfires when a drag ends, and on each key press or label click- Tests
- Unit tests; end-to-end in Chromium, Firefox and WebKit, on desktop and phone viewports: slider
- Source
- src/components/slider
When to use#
A Slider suits a value where the position matters more than the exact digits: a threshold, a time window, a level. People can drag it, tap a mark, or step it from the keyboard, and the current value shows next to the label. When people know the exact number they want, a NumberInput is quicker.
Similar#
- NumberInput is for exact numbers that are typed.
- Radio shows a handful of named options as a list; a Slider with named marks keeps them on one scale.
- Progress shows a value but takes no input.
One value#
A number in value gives one thumb. formatValue sets how the value reads next to the label and to screen readers. onValueChange fires on every step of a drag; onValueCommit fires once when the drag ends, and on each key press, which suits saving.
Saved: 30 s
import { useState } from "react";
import { Slider } from "cennge-ui";
export default function TimeoutSlider() {
const [seconds, setSeconds] = useState(30);
const [saved, setSaved] = useState(30);
return (
<div className="flex w-full max-w-96 flex-col gap-3">
<Slider
label="Request timeout"
value={seconds}
onValueChange={setSeconds}
onValueCommit={setSaved}
min={5}
max={120}
step={5}
formatValue={(value) => `${value} s`}
/>
<p className="text-sm text-muted">Saved: {saved} s</p>
</div>
);
}A range#
An array gives a thumb per item. The thumbs are named after the label, "Changed lines Minimum" and "Changed lines Maximum"; thumbLabels renames or translates the part after the label.
Pull requests that change 50 to 400 lines
import { useState } from "react";
import { Slider } from "cennge-ui";
export default function ChangedLinesFilter() {
const [lines, setLines] = useState([50, 400]);
const [from = 0, to = 1000] = lines;
return (
<div className="flex w-full max-w-96 flex-col gap-3">
<Slider label="Changed lines" value={lines} onValueChange={setLines} min={0} max={1000} step={10} />
<p className="text-sm text-muted">
Pull requests that change {from} to {to} lines
</p>
</div>
);
}Marks#
Each mark puts a dot on the track; a mark with a label also writes it under the track, and a click on that label moves the thumb there. A plain number, like 90 here, is a dot without a label. Dots on the filled part of the track change colour.
import { Slider } from "cennge-ui";
const percents = [0, 25, 50, 75, 100].map((value) => ({ value, label: `${value}%` }));
export default function CoverageSlider() {
return (
<Slider
label="Coverage threshold"
defaultValue={80}
marks={[...percents, 90]}
formatValue={(value) => `${value}%`}
className="w-full max-w-96"
/>
);
}Only on the marks#
restrictToMarks makes the marks the only values: a drag snaps to the nearest one and the arrow keys go from mark to mark, so steps need not be even.
import { Slider } from "cennge-ui";
const jobs = [1, 2, 4, 8, 16].map((value) => ({ value, label: String(value) }));
export default function ParallelJobs() {
return (
<Slider
label="Parallel jobs"
defaultValue={4}
min={1}
max={16}
marks={jobs}
restrictToMarks
color="secondary"
className="w-full max-w-96"
/>
);
}Named steps#
When every value the slider can take has a text label, the label becomes the value's name: the readout and screen readers say "Warning", not "1".
import { Slider } from "cennge-ui";
const levels = ["Error", "Warning", "Info", "Debug"].map((label, value) => ({ value, label }));
export default function LogLevel() {
return <Slider label="Log level" defaultValue={1} min={0} max={3} marks={levels} className="w-full max-w-96" />;
}Accessibility#
Each thumb is a slider with its value, minimum and maximum, and a text value from formatValue or a mark's label. A single thumb takes its name from label, or from aria-label when there is no visible label. The labels under the track are hidden from screen readers: they repeat what the thumbs already say, and the keyboard reaches every value without them. A disabled slider ignores clicks on its labels.