Skip to content

RangeSlider

A dual-thumb slider that selects a min to max interval on one track, with paired number fields for exact entry. The thumbs can meet but never cross.

Import

import { RangeSlider } from "@hyzer-labs/ui"

Demo

Two thumbs, one track, and the fill spans the selected interval. Drag a thumb past its partner and it clamps: the pair can meet but never cross. Typing in a number field clamps the same way, against the partner's value.

Hole length
let lengthMin = $state(200);
let lengthMax = $state(450);

<RangeSlider
	name="length"
	label="Hole length"
	min={100}
	max={900}
	unit="ft"
	bind:valueMin={lengthMin}
	bind:valueMax={lengthMax}
/>

Props

NameTypeDefaultNote
namestringRequired. Base name — the thumbs submit as {name}-min and {name}-max.
labelstringRequired. Rendered as the fieldset legend; sr-only with hideLabel.
minnumber0
maxnumber100
stepnumber1
valueMinnumberminBindable. The lower thumb.
valueMaxnumbermaxBindable. The upper thumb.
showInputbooleantrueRenders the pair of exact-entry number fields.
ticksSliderTick[]Decorative marks under the track. See SliderTick below.
unitstringOne visual suffix after the pair; rendered aria-hidden.
minThumbLabelstring`${label} (minimum)`Accessible name for the lower thumb (and its number field).
maxThumbLabelstring`${label} (maximum)`Accessible name for the upper thumb (and its number field).
orientation'horizontal' | 'vertical''horizontal'Vertical uses writing-mode: vertical-lr (bottom-up growth) on both ranges; horizontal is unchanged.
inputPosition'start' | 'end''end'Logical on both axes. The two exact-entry fields stay one inline cluster, placed above or below the track in vertical rather than stacked to mirror the thumbs.
descriptionstringHelp text below the legend.
errorstringInline error message; sets the error state.
requiredbooleanfalseLegend indicator only.
disabledbooleanfalseDisables all four inputs.
hideLabelbooleanfalse
classstringMerged after the hz-field hz-field--slider hz-field--slider-range classes.

SliderTick

NameTypeDefaultNote
valuenumberRequired. A bare number is shorthand for an unlabeled tick. Out-of-range ticks are skipped.
labelstringSmall text under the mark.

Theme hooks

What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.

Root class: .hz-field hz-field--slider hz-field--slider-range

Data attributes

HookValuesStyles
data-state'default' | 'error' | 'disabled'The universal field state hook, on the root. Precedence is fixed: error beats disabled.
data-has-tickspresent when ticks renderOn .hz-slider-row, as on Slider.
data-has-inputpresent when the number fields showOn .hz-slider-row. Unstyled by the reference theme.
data-orientation'horizontal' | 'vertical'On .hz-slider-row; always present, as on Slider. Both ranges pick up writing-mode: vertical-lr; direction: rtl.
data-input-position'start' | 'end'On .hz-slider-row; always present, as on Slider. The min–max pair stays one inline cluster, reordered as a unit.

Custom properties

HookValuesStyles
--hz-slider-track-height<length> — default 0.375remTrack thickness.
--hz-slider-thumb-size<length> — default 1.125remThumb width and height. The fill edge tracks the thumb centre, so the fill and ticks stay aligned at any size.
--hz-slider-length<length> — default 12remThe track's block length in vertical orientation. Ignored in horizontal, where the track flexes to fill the row instead.

Part classes

HookValuesStyles
.hz-field-labelchild elementThe label.
.hz-field-requiredchild elementThe required marker.
.hz-field-descriptionchild elementThe hint text.
.hz-field-errorchild elementThe error message.
.hz-slider-rowchild elementThe track-and-readout row.
.hz-slider-trackchild elementPaints the track and the between-thumbs fill.
.hz-slider-minthe lower range inputCarries .hz-slider too.
.hz-slider-maxthe upper range inputCarries .hz-slider too.
.hz-slider-inputschild elementWraps the number-field pair (or the readout) so it stays one inline cluster regardless of orientation.
.hz-slider-number-minchild elementThe lower number field.
.hz-slider-number-maxchild elementThe upper number field.
.hz-slider-sepchild elementThe dash between the readouts.
.hz-slider-tickschild elementThe tick strip.

Accessibility

The group is a <fieldset> whose label renders as the <legend>. Each thumb is a real range input with its own accessible name (minThumbLabel/maxThumbLabel) and native slider semantics. Tab reaches both thumbs, arrow keys step them, and a thumb dragged past its partner clamps rather than crossing.

The number fields carry derived accessible names and never submit. The two thumbs submit as the base name plus -min/-max suffixes.

description and error chain into aria-describedby on both ranges, and an error also sets aria-invalid on both. required renders the legend indicator only. aria-required is not part of the slider role's supported ARIA attributes, so it is never applied.

Ticks, the separator, and the readout are decorative and aria-hidden. In vertical orientation the component stamps aria-orientation="vertical" on both ranges explicitly, since writing-mode does not reliably flip a native range's implicit horizontal orientation in the accessibility tree across engines and assistive tech.

References: APG Slider (Multi-Thumb) pattern · MDN: <input type="range">