Slider
Picks a number from a range, following the
ARIA slider pattern. The
thumb carries role="slider" and the value, and takes the keyboard. The track
takes the pointer. An array value gives one thumb per value, following the
multi-thumb slider pattern.
import {
Slider,
SliderLabel,
SliderThumb,
SliderTrack,
SliderStateChild,
useSliderState,
} from 'terracotta/slider';Anatomy
<Slider> {/* owns the value, min, max and step */}
<SliderLabel/> {/* optional; names the thumb */}
<SliderTrack> {/* press and drag to set the value */}
<SliderThumb/>{/* role="slider", focusable */}
</SliderTrack>
</Slider>Put the thumb inside the track, so dragging the thumb works like dragging the track.
Examples
Basic
The thumb and the filled range are placed from percent(). The second slider
has step={0.5}, largeStep={4} and an aria-valuetext formatter.
Placing the thumb
Terracotta moves the value, not the thumb. Read percent(), from 0 to 100,
and place the thumb with it:
<Slider defaultValue={40}>
{state => (
<SliderTrack class="slider-track">
<SliderThumb
class="slider-thumb"
style={{ 'inset-inline-start': `${state.percent()}%` }}
/>
</SliderTrack>
)}
</Slider>.slider-track { position: relative; touch-action: none; }
.slider-thumb { position: absolute; translate: -50% -50%; inset-block-start: 50%; }touch-action: none on the track stops a touch drag from scrolling the page.
Value text
When the number alone is not clear, getValueText sets aria-valuetext:
<Slider defaultValue={3} min={1} max={5} getValueText={value => `${value} of 5 stars`}>
…
</Slider>Controlled
const [volume, setVolume] = createSignal(40);
<Slider value={volume()} onChange={setVolume}>…</Slider>Vertical
vertical makes the track run from bottom to top, and sets
aria-orientation="vertical" on the thumb. Place the thumb with
inset-block-end instead.
Without a visible label
Leave out SliderLabel and name the thumb directly:
<SliderThumb aria-label="Zoom" />Several thumbs
Pass an array as the value, and render one SliderThumb per value with its
index:
- The thumbs cannot pass each other. Each thumb's
aria-valueminandaria-valuemaxare the values of the thumbs beside it, and they update as those thumbs move. - Each thumb is in the tab sequence, in
indexorder, wherever it is on screen. SliderLabelnames the group, which getsrole="group". Give each thumb a name of its own, such asaria-label="Minimum price".- A press on the track moves the thumb nearest the pointer.
onChangereceives the whole array.getValueTextreceives each thumb's value andindex.- A starting value given out of order is sorted.
<Slider defaultValue={[200, 650]} max={1000} step={10}>
{state => (
<SliderTrack class="slider-track">
<SliderThumb index={0} aria-label="Minimum price"
style={{ 'inset-inline-start': `${state.percent(0)}%` }} />
<SliderThumb index={1} aria-label="Maximum price"
style={{ 'inset-inline-start': `${state.percent(1)}%` }} />
</SliderTrack>
)}
</Slider>In a form
name adds a hidden input that carries the value, one per thumb:
<Slider defaultValue={40} name="volume">…</Slider>State attributes
| Element | Attribute | Present when |
|---|---|---|
Slider |
tc-slider |
Always |
SliderLabel |
tc-slider-label |
Always |
SliderTrack |
tc-slider-track |
Always |
SliderThumb |
tc-slider-thumb |
Always |
Slider |
tc-vertical |
The slider is vertical |
Slider, SliderTrack, SliderThumb |
tc-disabled |
The slider is disabled |
Reading the state in code
| Member | Type | Description |
|---|---|---|
value() |
number |
The value of the first thumb. |
values() |
number[] |
The values of every thumb. |
min() / max() / step() |
number |
The range and step. |
percent(index?) |
number |
Where a thumb's value sits in the range, from 0 to 100. |
disabled() |
boolean |
Whether the slider is disabled. |
setValue(value) |
(number) => void |
Sets the first thumb's value, snapped to the step and kept in range. |
setThumbValue(index, value) |
(number, number) => void |
Sets one thumb's value, kept between its neighbours. |
thumbMin(index) / thumbMax(index) |
number |
The range a thumb can move in. |
increment(steps?, index?) / decrement(steps?, index?) |
(number?, number?) => void |
Moves a thumb by a number of steps, 1 by default. |
You can reach this state as the render-prop argument on Slider,
SliderTrack or SliderThumb, through <SliderStateChild>, or with
useSliderState() inside any descendant component.
Keyboard
Handled on the thumb:
| Key | Action |
|---|---|
| → / ↑ | Increases the value by one step |
| ← / ↓ | Decreases the value by one step |
| Page Up / Page Down | Changes the value by largeStep steps |
| Home / End | Sets the lowest / highest value the thumb can take |
API
<Slider>
Renders a <div> by default. Pass exactly one of defaultValue or value.
Both take a number, or an array for several thumbs.
| Prop | Type | Default | Description |
|---|---|---|---|
as |
ValidConstructor |
'div' |
Element or component to render as. |
defaultValue |
number | number[] |
none | Initial value, uncontrolled. |
value |
number | number[] |
none | Current value, controlled. |
min |
number |
0 |
The lowest value. |
max |
number |
100 |
The highest value. |
step |
number |
1 |
The distance between allowed values. |
largeStep |
number |
10 |
Steps moved by Page Up and Page Down. |
vertical |
boolean |
false |
Runs the slider from bottom to top. |
disabled |
boolean |
false |
Blocks changes. |
getValueText |
(value: number, index: number) => string |
none | Text for each thumb's aria-valuetext. |
name |
string |
none | Submits the value with a form, one input per thumb. |
onChange |
(value: number | number[]) => void |
none | Called with the new value, in the same shape as value. |
children |
JSX.Element | (state: SliderStateProperties) => JSX.Element |
none | Contents, or a render prop. |
| …rest | props of as |
none | Forwarded to the rendered element. |
<SliderLabel>
Renders a <label> by default. The thumb points aria-labelledby at it while
it is mounted. Clicking it focuses the thumb. With several thumbs, the slider
gets role="group" and the label names the group instead.
<SliderTrack>
Renders a <div> by default. Pressing it sets the value under the pointer and
focuses the thumb. Dragging keeps setting it until the pointer is released.
With several thumbs, the press moves the thumb nearest the pointer.
<SliderThumb>
Renders a <div> by default.
| Prop | Type | Default | Description |
|---|---|---|---|
index |
number |
0 |
Which value the thumb sets, with several thumbs. |
Rendered attributes: role="slider", tabindex="0" (-1 while disabled),
aria-valuenow, aria-valuemin and aria-valuemax (limited by the
neighbouring thumbs), aria-valuetext when
getValueText is set, aria-orientation="vertical" when vertical,
aria-labelledby while a SliderLabel is mounted and there is one thumb,
aria-disabled,
tc-slider-thumb.
All three parts take as, ref, children (or a render prop) and forward
the rest of their props.