Skip to main content

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.

Loading example

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.

Loading example

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.

Loading example

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.

Loading example

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.

Loading example

Props

SliderProps extends the <input> attributes (name, id, onBlur, aria-*, …) minus type, value, defaultValue, onChange, size, min, max, step and className, with:

PropTypeDefaultDescription
valuenumberControlled value
defaultValuenumberminUncontrolled initial value
onChange(value: number) => voidCalled with the number, not the event
minnumber0
maxnumber100
stepnumber1
showValuebooleanfalseReadout beside the label
formatValue(value: number) => stringFormats the readout and aria-valuetext
marksArray<number | {value: number; label?: ReactNode}>Ticks under the track; data-active up to the value
labelReactNodeVisible label wired with for / aria-labelledby
descriptionReactNodeHelper text wired with aria-describedby
disabledbooleanFieldset / FormItem disabled
invalidbooleanFormItem invalidaria-invalid, data-invalid and destructive fill and thumb
size'sm' | 'md' | 'lg''md'
fullWidthbooleantrue
classNamestringMerged onto the root
inputClassNamestringMerged onto the native range input; utilities win over the track and thumb rules
styleCSSPropertiesApplied to the root

Slots

SlotElementNotes
[data-slot="slider"]divRoot. Also data-size and Headless UI's data-disabled, data-invalid, data-focus, data-hover mirrored from the input
[data-slot="slider-header"]divLabel and readout row
[data-slot="slider-label"]labelThe label; data-disabled / data-invalid mirrored
[data-slot="slider-value"]spanThe readout; aria-hidden
[data-slot="slider-control"]inputThe range input; drives --slider-value
[data-slot="slider-marks"]divTick container; aria-hidden
[data-slot="slider-mark"]spanOne tick; data-active at or below the value
[data-slot="slider-mark-tick"]spanThe tick line
[data-slot="slider-mark-label"]spanOptional text under the tick
[data-slot="slider-description"]pHelper 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

TokenUsed for
--primary, --mutedFilled and empty track, thumb
--ring, --backgroundKeyboard focus ring around the thumb
--destructiveFill, thumb and ring while invalid
--radius-fullTrack and thumb shape
--spacingThumb and track size per size, minimum 24px hit target
--duration-fastThumb hover and press transition

Accessibility

  • A native range input: Arrow keys move by step, Page Up/Down by a larger amount, Home/End to min/max.
  • label is a real <label for> and is also joined into aria-labelledby; description joins aria-describedby. Both compose with a FormItem's own ids.
  • aria-valuetext carries formatValue output, 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 --ring ring around the thumb; invalid recolours the ring with --destructive.
  • Pointer target is at least 24px tall for every size (WCAG 2.5.8).
  • NumberInput: type an exact value instead of dragging.
  • Progress: the read-only counterpart.
  • FormItem: label, description and message wiring.