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.
The Slider component allows users to select one or multiple values from a defined range by dragging one or more handles (thumbs) along a track. It is commonly used for adjusting numeric values, setting limits, or selecting ranges in a visual and intuitive way.
Sliders can be configured to support:
- A single handle (e.g. volume, brightness, zoom)
- Multiple handles (e.g. selecting multiple thresholds)
- Range selection (minimum and maximum values)
They provide immediate visual feedback and are ideal when users benefit from seeing relative positions within a range rather than entering precise numbers manually.
When to use it (and when not to)
Section titled “When to use it (and when not to)”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 (e.g. price filters, time ranges).
Avoid sliders 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 — consider pairing the slider with an input field.
Quick summary of accessibility & consistency
Section titled “Quick summary of accessibility & consistency”- Ensure the slider is fully operable via keyboard (arrow keys, Home/End).
- Provide ARIA roles and attributes (e.g.
role="slider",aria-valuemin,aria-valuemax,aria-valuenow). - Make sure labels are programmatically associated with the slider.
- Maintain sufficient color contrast for the track, handles, and focus states.
- Provide visible focus indicators for keyboard users.
- Announce value changes appropriately for screen readers.
Properties
Section titled “Properties”Anatomy
Section titled “Anatomy”spacing-2424pxspacing-2424pxspacing-044pxspacing-044pxradius-xl1000pxradius-xl1000pxspacing-044pxspacing-044pxradius-xl1000pxradius-xl1000pxspacing-2424pxspacing-2424pxradius-xl1000pxradius-xl1000pxState : Rested
Section titled “State : Rested”color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000color-fill-contrast-emphasis#ffffffcolor-fill-contrast-emphasis#ffffffcolor-border-contrast-default#d4d2d1color-border-contrast-default#e4e4e4State : Active
Section titled “State : Active”color-fill-neutral-subtle#e5e3e1color-fill-neutral-subtle#6c7073color-fill-contrast-emphasis#ffffffcolor-fill-contrast-emphasis#ffffffState : Disabled
Section titled “State : Disabled”color-fill-contrast-default#f5f5f5color-fill-contrast-default#e4e4e4color-fill-neutral-muted#f5f5f5color-fill-neutral-muted#a8aaacVariant : Range
Section titled “Variant : Range”color-fill-contrast-default#f5f5f5color-fill-contrast-default#e4e4e4color-fill-neutral-emphasis#000000color-fill-neutral-emphasis#000000Variant : EV
Section titled “Variant : EV”color-border-ev-default#c57046color-border-ev-default#0072f0Styling
Section titled “Styling”Colors
Section titled “Colors”| Name | Applied as | Applied to |
|---|---|---|
color-fill-neutral-emphasis | Background color | Track, Progress, Thumb |
color-fill-neutral-emphasis | Background color | Track, Progress, Thumb |
color-fill-neutral-subtle | Background color | Thumb (active) |
color-fill-neutral-subtle | Background color | Thumb (active) |
color-fill-neutral-muted | Background color | Thumb (disabled dot) |
color-fill-neutral-muted | Background color | Thumb (disabled dot) |
color-fill-contrast-emphasis | Dot color | Thumb |
color-fill-contrast-emphasis | Dot color | Thumb |
color-fill-contrast-default | Background color | Track (range), Thumb (disabled) |
color-fill-contrast-default | Background color | Track (range), Thumb (disabled) |
color-border-contrast-default | Border color | Thumb |
color-border-contrast-default | Border color | Thumb |
color-border-ev-default | Background color | Track (EV variant) |
color-border-ev-default | Background color | Track (EV variant) |
Spacing
Section titled “Spacing”| Name | Applied as | Applied to |
|---|---|---|
spacing-04 | Height | Track, Progress |
spacing-04 | Height | Track, Progress |
spacing-24 | Size | Thumb, Gap |
spacing-24 | Size | Thumb, Gap |
radius-xl | Border radius | Track, Progress, Thumb |
radius-xl | Border radius | Track, Progress, Thumb |
- 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 (e.g. 5, 10, 15).
- Use range sliders when users need to define both minimum and maximum bounds.
- Don’t use sliders for precise data entry (use input fields instead).
- Don’t overload the slider with too many handles — it reduces clarity.
- Don’t hide value feedback; users should always know what they’re selecting.
- Don’t rely on color alone to communicate state or limits.
- Don’t make sliders too small to interact with comfortably.
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>Schemes
Section titled “Schemes”<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.