Slider
Sliders let people select a value from a range by dragging a handle along a track. Reach for one when the position within the range matters more than the exact number, as with volume, brightness or a price filter.
The component wraps a native <input type="range">, so keyboard support, form
participation and the slider role come from the platform. Pointer input is handled by the
component itself, which is what lets a range slider grab the nearer of its two handles.
Usage
value is bindable and defaults to 0; the range is min/max (0 to 100 unless you say otherwise).
<Slider value={40} aria-label="Volume" />Variants
Three ways to read the track, all sharing the same handle and gap anatomy:
- Standard fills from the start of the track to the handle.
- Centered fills from the midpoint outward, so the handle reads as an offset from neutral. Good for balance, EQ or an exposure correction.
- Range has two handles and fills between them. Bind
valueandendValue; the handles cannot cross.
<Slider value={40} aria-label="Standard" />
<Slider centered value={20} aria-label="Centered" />
<Slider
range
value={30}
endValue={70}
aria-label="Minimum price"
endInputAttributes={{ 'aria-label': 'Maximum price' }}
/>Sizes
Five sizes, from xs to xl. The size sets the track height, the handle
height and the track corner radius together. Bigger tracks get proportionally less rounding, which is why an xl slider reads as a rounded rectangle rather than a pill.
The default is xs.
<Slider size="xs" value={50} />
<Slider size="s" value={50} />
<Slider size="m" value={50} />
<Slider size="l" value={50} />
<Slider size="xl" value={50} />| Size | Track height | Handle height | Track corner | Icon size |
|---|---|---|---|---|
xs | 16dp | 44dp | 8dp | n/a |
s | 24dp | 44dp | 8dp | n/a |
m | 40dp | 44dp | 12dp | 24dp |
l | 56dp | 68dp | 16dp | 24dp |
xl | 96dp | 108dp | 28dp | 32dp |
Orientation
orientation="vertical" runs the track bottom to top. The length comes from --np-slider-length (12rem by default), and the native input follows the
same writing mode, so ↑/↓ keep working the way you would expect.
<Slider orientation="vertical" value={60} aria-label="Brightness" />
<Slider orientation="vertical" size="l" --np-slider-length="9rem" value={60} />Steps and stop indicators
Set step to make the slider discrete. Add ticks to draw a stop indicator on
every step; the tick under the handle is omitted so the gap around the handle stays clean, and the ticks
at the two ends give way to the track's own stop indicators.
A discrete handle travels between the two track corners rather than the full track, which is what keeps it aligned with the tick it selects.
<Slider step={25} ticks value={50} aria-label="Rating" />
<Slider step={10} ticks size="m" value={40} aria-label="Volume in tens" />Value indicator
labeled shows a value indicator above the handle on hover, focus and drag. The
container grows with its content, so format is free to return whatever reads best.
A continuous slider reports the exact position it was dragged to, which is rarely something you
want to put in front of someone. Pair labeled with a step, or round in format.
<Slider labeled step={1} value={40} aria-label="Volume" />
<Slider labeled size="m" value={60} format={(v) => `${Math.round(v)}%`} aria-label="Brightness" />Inset icon
The icon snippet is drawn inside the leading end of the active track. Once the active
track runs out of room for it, which happens at a low value or between the handles of a range slider, the icon moves over to the inactive track instead of sitting there half
clipped, and takes the inactive track's colour with it. It needs room either way, so it is meant
for m and larger.
The snippet is yours to render, so a slider at zero can show a different icon: drag the volume
slider below to the start and the icon becomes volume_off.
<Slider size="m" bind:value={volume} aria-label="Volume">
{#snippet icon()}
<Icon>{volume === 0 ? 'volume_off' : 'volume_up'}</Icon>
{/snippet}
</Slider>Disabled
<Slider disabled value={40} aria-label="Disabled" />Right-to-left
Horizontal sliders mirror themselves in a right-to-left context. The track fills from the right
and the stop indicator moves to the left edge. There is nothing to configure, since it follows the
inherited dir.
<div dir="rtl">
<Slider value={60} />
</div>Theming
| Token | Default value |
|---|---|
--np-slider-active-track-color | --np-color-primary |
--np-slider-inactive-track-color | --np-color-secondary-container |
--np-slider-handle-color | --np-color-primary |
--np-slider-active-stop-color | --np-color-on-primary |
--np-slider-inactive-stop-color | --np-color-on-secondary-container |
--np-slider-label-container-color | --np-color-inverse-surface |
--np-slider-label-text-color | --np-color-inverse-on-surface |
--np-slider-track-height | per size, 1rem at xs |
--np-slider-track-shape | per size, 0.5rem at xs |
--np-slider-track-inside-shape | 0.125rem |
--np-slider-handle-height | per size, 2.75rem at xs |
--np-slider-handle-width | 0.25rem |
--np-slider-handle-shape | --np-shape-corner-full |
--np-slider-stop-indicator-size | 0.25rem |
--np-slider-icon-size | per size, 1.5rem at m |
--np-slider-length | 12rem, vertical only |
--np-slider-icon-color | --np-color-on-primary |
--np-slider-icon-inactive-color | --np-color-on-secondary-container |
--np-slider-icon-padding | 0.625rem |
--np-slider-handle-width-focus | 0.125rem |
--np-slider-disabled-active-track-color | --np-color-on-surface |
--np-slider-disabled-inactive-track-color | --np-color-on-surface |
--np-slider-disabled-handle-color | --np-color-on-surface |
The handle narrows to --np-slider-handle-width-focus while it is pressed or focused, and
keeps its full height either way. The disabled colours are blended with an opacity of their own, so
they take a colour, not a pre-faded one.
Example
<Slider
--np-slider-active-track-color="var(--np-color-error)"
--np-slider-inactive-track-color="var(--np-color-error-container)"
--np-slider-handle-color="var(--np-color-error)"
--np-slider-track-shape="0.25rem"
value={40}
/>Accessibility
The slider has no text of its own, so pass an aria-label (or aria-labelledby) describing what it controls. A range slider has two
inputs: the spread attributes go to the start handle, while endInputAttributes covers the end handle. Label both.
Arrow keys move by one step, Home/End jump to the ends, and Page Up/Page Down move in larger increments. All of that comes from the native input.
A format function feeds aria-valuetext, so a screen reader announces 21 percent rather than the bare 21. Reach for it whenever the number needs a
unit to make sense.
API
Attributes
Anything not listed here is forwarded to the underlying <input type="range">.
| Attribute | Type | Default | Description |
|---|---|---|---|
min | number | 0 | Lowest selectable value. |
max | number | 100 | Highest selectable value. |
step | number | 0 | Step increment. 0 makes the slider continuous. |
size | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xs' | Track height, handle height and corner radius. |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Vertical sliders run bottom to top. |
range | boolean | false | Two handles selecting a sub-range. Bind value and endValue. |
centered | boolean | false | Grows the active track out of the midpoint instead of the start. |
labeled | boolean | false | Shows the value indicator on hover, focus and drag. |
ticks | boolean | false | Draws a stop indicator on every step. Requires step. |
disabled | boolean | false | Disables the slider. |
format | (value: number) => string | String | Formats the value indicator. |
icon | Snippet | undefined | Icon drawn inside the leading end of the active track. |
endInputAttributes | HTMLInputAttributes | undefined | Attributes for the end handle's input. range only. |
Bindables
| Attribute | Type | Description |
|---|---|---|
value | number | Current value. With range, the start handle's value. |
endValue | number | undefined | End handle's value. range only; defaults to max. |
element | HTMLDivElement | The slider's root element. |
inputElement | HTMLInputElement | The start handle's input. |
endInputElement | HTMLInputElement | The end handle's input. range only. |