Slider
A Slider component allows users to select one or multiple values from a defined range by dragging one or more handles along a track.
Ready to use
The Slider component allows users to select one or multiple values from a defined range by dragging one or more handles along a track. It’s commonly used for adjusting numeric values, setting limits, or selecting ranges in a visual and intuitive way, and provides immediate visual feedback when users benefit from seeing relative positions rather than entering precise numbers manually.
Anatomy
Section titled “Anatomy”- Track
- Progress
- Thumb
When to use it
Section titled “When to use it”Use a slider when:
- Users need to adjust a value within a known, limited range.
- You want to provide a quick and interactive way to modify settings.
- Visual comparison across a range adds clarity (price filters, time ranges).
Avoid a slider when:
- Users must enter highly precise or arbitrary values.
- The range is very large or undefined.
- Accessibility or keyboard-only input is the primary interaction — pair the slider with an input field in that case.
Properties
Section titled “Properties”State
Rested, Active, and Disabled. Active swaps the thumb’s fill while the user drags or presses; Disabled mutes the thumb and progress fill but keeps the track visible so the user knows what’s there.
Handles
Single handle for one value (volume, brightness, zoom), or multiple handles for thresholds and ranges (price filter min/max). The thumb size and border treatment stay identical regardless of handle count.
Variant
Three variants on the track itself: Default uses the neutral emphasis fill, Range uses a contrast fill for the inactive segments to highlight the selected range, and EV uses the EV brand colour for charging or efficiency tools.
Platform considerations
Section titled “Platform considerations”Desktop
Mouse and keyboard both work — arrow keys move the thumb in discrete steps, Home/End jump to the bounds. Pair with an input field when users need precise entry alongside the visual.
Tablet
Touch and pointer both work. Keep the thumb size large enough that fingers can grab it without overlapping adjacent handles.
Mobile
Touch-first. The 24 px thumb stays comfortable to grab; consider pairing with an input field when the value needs precise editing on a small screen.
Best practices
Section titled “Best practices”Sliders trade precision for feel — make the current value visible so users know what they’ve picked.
Do
Provide clear labels for the slider and its values (min, max, current), use sensible defaults that reflect common or recommended values, show the current value near the handle or in a linked input, snap to steps when values should be discrete (5, 10, 15), and use range sliders when users need to define both minimum and maximum bounds.
Don't
Don’t use sliders for precise data entry (use input fields instead), don’t overload the slider with too many handles, don’t hide value feedback (users should always know what they’re selecting), don’t rely on colour alone to communicate state or limits, and don’t make sliders too small to interact with comfortably.
Content guidelines
Section titled “Content guidelines”Label the slider with the dimension it adjusts (“Price range”, “Brightness”) and pair it with a clearly visible value read-out. Use the same units in the label and the read-out — “€20,000 – €40,000” reads better than mixing currency symbols and “k” abbreviations across the slider and its label. For range sliders, name both ends (“Min”, “Max”) near the handles so users can tell them apart without guessing.
Elements
Section titled “Elements”<div class="tng-slider"> <div class="tng-slider-track"></div></div>Labels
Section titled “Labels”<div class="tng-slider"> <div class="tng-slider-labels"> <div style="--tng-slider-label-value: 0.5">50%</div> </div> <div class="tng-slider-bar"> <div class="tng-slider-track"></div> <div class="tng-slider-thumb" style="--tng-slider-thumb-value: 0.5" ></div> </div> <div class="tng-slider-labels"> <div style="--tng-slider-label-value: 0">0%</div> <div style="--tng-slider-label-value: 0.5">50%</div> <div style="--tng-slider-label-value: 1">100%</div> </div></div>Progress
Section titled “Progress”<div class="tng-slider"> <div class="tng-slider-bar"> <div class="tng-slider-track"></div> <div class="tng-slider-progress" style=" --tng-slider-progress-start: 0.5; --tng-slider-progress-end: 0.75; " ></div> </div></div><div class="tng-slider"> <div class="tng-slider-bar"> <div class="tng-slider-track"></div> <div class="tng-slider-thumb"></div> <div class="tng-slider-thumb" style="--tng-slider-thumb-value: 0.25" ></div> <div class="tng-slider-thumb" style="--tng-slider-thumb-value: 0.5" ></div> </div></div>Active
Section titled “Active”<div class="tng-slider"> <div class="tng-slider-bar"> <div class="tng-slider-track"></div> <div class="tng-slider-thumb is-active" style="--tng-slider-thumb-value: 0.5" ></div> </div></div>Disabled
Section titled “Disabled”<div class="tng-slider"> <div class="tng-slider-bar"> <div class="tng-slider-track"></div> <div class="tng-slider-thumb is-disabled" style="--tng-slider-thumb-value: 0.5" ></div> </div></div>Variants
Section titled “Variants”<div class="tng-slider"> <div class="tng-slider-track"></div></div><div class="tng-slider is-ev"> <div class="tng-slider-track"></div></div><div class="tng-slider is-range"> <div class="tng-slider-track"></div></div><div class="tng-slider is-temperature"> <div class="tng-slider-track"></div></div>Range Input
Section titled “Range Input”<div class="tng-slider"> <input style="--tng-slider-progress-value: 0.5" type="range" /></div>With datalist
Section titled “With datalist”A <datalist> linked via list / id provides snap-to-step behavior on the native range input. Hide it with .sr-only since rendering is inconsistent across browsers, and use a separate .tng-slider-labels div with role="presentation" for the visual labels.
<div class="tng-slider"> <input list="slider-options" type="range" min="0" max="100" step="25" /> <datalist class="sr-only" id="slider-options"> <option value="0"></option> <option value="25"></option> <option value="50"></option> <option value="75"></option> <option value="100"></option> </datalist> <div class="tng-slider-labels" role="presentation"> <div style="--tng-slider-label-value: 0">0%</div> <div style="--tng-slider-label-value: 0.25">25%</div> <div style="--tng-slider-label-value: 0.5">50%</div> <div style="--tng-slider-label-value: 0.75">75%</div> <div style="--tng-slider-label-value: 1">100%</div> </div></div>Disabled
Section titled “Disabled”<div class="tng-slider"> <input disabled type="range" /></div>For designers
Section titled “For designers”- All states (rested, disabled) must maintain accessible contrast.
- Focus styles must remain visible and meet WCAG AA contrast requirements across all supported surfaces.
- Colour tokens used should be semantic (e.g.
foreground/neutral/default) rather than fixed values to ensure the component adapts correctly to different themes or surfaces.
For developers
Section titled “For developers”Semantic markup
Section titled “Semantic markup”Use <input type="range"> whenever possible — it provides role="slider" implicitly, with built-in aria-valuemin, aria-valuemax, and aria-valuenow derived from the min, max, and value attributes.
When using the custom div-based elements (.tng-slider-thumb, .tng-slider-track) for visual purposes, an underlying <input type="range"> should still drive the semantics. If a native input cannot be used, apply role="slider" manually together with the required ARIA properties.
Labels
Section titled “Labels”Associate a visible label with the input via <label for="..."> or aria-label / aria-labelledby. The top value labels (.tng-slider-labels) are visual — they do not provide an accessible name by themselves.
Use aria-valuetext when the numeric value alone is not meaningful (e.g. aria-valuetext="50%" or aria-valuetext="Moderate").
Keyboard interaction
Section titled “Keyboard interaction”| Key | Action |
|---|---|
| ← / → | Decrease / increase value by one step |
| ↑ / ↓ | Decrease / increase value by one step |
| Home / End | Set to minimum / maximum value |
| Page Up / Page Down | Increase / decrease by a larger step (browser-defined) |
Native <input type="range"> handles all of the above automatically.
Disabled state
Section titled “Disabled state”Using the native disabled attribute on <input type="range"> removes it from the tab order and announces it as disabled to assistive technologies.
The .is-disabled class on .tng-slider-thumb is visual only — pair it with aria-disabled="true" if used on a custom role="slider" element.
Datalist
Section titled “Datalist”Use .sr-only on <datalist> to visually hide it — browser rendering of datalist for range inputs is inconsistent (Safari renders nothing, Firefox ignores positioning). The list / id linkage still provides snap-to-step behavior where supported.
The visual .tng-slider-labels div should use role="presentation" since the labels are decorative duplicates of the datalist values and should not be announced separately.