Slider
A slider is a native <input type="range"> rendered through Headless UI's
Input, so a FormItem wires its ids and an enclosing Fieldset disables
it. The track and thumb are styled through the vendor pseudo-elements in
styles/slider.css, reading --primary, --muted, --ring and
--radius-full, so a retheme moves the slider with every other control.
import Slider from '@zuilib/components/slider'
Basic
label renders a <label for>; showValue adds a readout beside it.
value / onChange for controlled, defaultValue for uncontrolled.
Sizes
sm, md and lg set the thumb diameter and track height as multiples of
--spacing. The input is always at least --spacing * 6 (24px) tall for a
usable pointer target.
Marks and formatting
marks draws ticks under the track, data-active up to the current value.
A mark with a label shows it under the tick. formatValue shapes the
readout and aria-valuetext; without it, a string mark label at the current
value is announced instead of the number.
States
invalid paints the fill and thumb with --destructive and sets
aria-invalid. disabled lowers the opacity and blocks the pointer; leave it
unset to inherit from a Fieldset / FormItem.
In a FormItem
Inside a FormItem the item's Label, Description and Message are wired
to the input, and its invalid becomes the slider's default. The slider's
own label / description add to the item's rather than replacing them,
so use one or the other.
Props
SliderProps extends the <input> attributes (name, id, onBlur,
aria-*, …) minus type, value, defaultValue, onChange, size,
min, max, step and className, with:
| Prop | Type | Default | Description |
|---|---|---|---|
value | number | — | Controlled value |
defaultValue | number | min | Uncontrolled initial value |
onChange | (value: number) => void | — | Called with the number, not the event |
min | number | 0 | |
max | number | 100 | |
step | number | 1 | |
showValue | boolean | false | Readout beside the label |
formatValue | (value: number) => string | — | Formats the readout and aria-valuetext |
marks | Array<number | {value: number; label?: ReactNode}> | — | Ticks under the track; data-active up to the value |
label | ReactNode | — | Visible label wired with for / aria-labelledby |
description | ReactNode | — | Helper text wired with aria-describedby |
disabled | boolean | Fieldset / FormItem disabled | |
invalid | boolean | FormItem invalid | aria-invalid, data-invalid and destructive fill and thumb |
size | 'sm' | 'md' | 'lg' | 'md' | |
fullWidth | boolean | true | |
className | string | — | Merged onto the root |
inputClassName | string | — | Merged onto the native range input; utilities win over the track and thumb rules |
style | CSSProperties | — | Applied to the root |
Slots
| Slot | Element | Notes |
|---|---|---|
[data-slot="slider"] | div | Root. Also data-size and Headless UI's data-disabled, data-invalid, data-focus, data-hover mirrored from the input |
[data-slot="slider-header"] | div | Label and readout row |
[data-slot="slider-label"] | label | The label; data-disabled / data-invalid mirrored |
[data-slot="slider-value"] | span | The readout; aria-hidden |
[data-slot="slider-control"] | input | The range input; drives --slider-value |
[data-slot="slider-marks"] | div | Tick container; aria-hidden |
[data-slot="slider-mark"] | span | One tick; data-active at or below the value |
[data-slot="slider-mark-tick"] | span | The tick line |
[data-slot="slider-mark-label"] | span | Optional text under the tick |
[data-slot="slider-description"] | p | Helper text |
/* Consumer CSS: a thicker, accent-coloured fill */
[data-slot="slider-control"] {
--slider-fill: var(--accent-foreground);
--slider-track-height: calc(var(--spacing) * 2.5);
}
Tokens
| Token | Used for |
|---|---|
--primary, --muted | Filled and empty track, thumb |
--ring, --background | Keyboard focus ring around the thumb |
--destructive | Fill, thumb and ring while invalid |
--radius-full | Track and thumb shape |
--spacing | Thumb and track size per size, minimum 24px hit target |
--duration-fast | Thumb hover and press transition |
Accessibility
- A native range input:
Arrowkeys move bystep,Page Up/Downby a larger amount,Home/Endtomin/max. labelis a real<label for>and is also joined intoaria-labelledby;descriptionjoinsaria-describedby. Both compose with aFormItem's own ids.aria-valuetextcarriesformatValueoutput, or a string mark label at the current value, so "Medium" is read rather than "50".- The readout and the marks are
aria-hidden; the input announces its own value. - Keyboard focus shows a
--ringring around the thumb;invalidrecolours the ring with--destructive. - Pointer target is at least 24px tall for every size (WCAG 2.5.8).
Related
- NumberInput: type an exact value instead of dragging.
- Progress: the read-only counterpart.
- FormItem: label, description and message wiring.