Skip to content

HySlider

Usage examples on this page are written for Lit / plain HTML (<hy-slider>). The same component ships as HySlider in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

A form-associated horizontal range slider for selecting a numeric value within a bounded range. Layers a custom track, fill, and thumb over a transparent native <input type="range"> to retain full accessibility and keyboard support (arrow keys, PageUp/Down, Home/End) while allowing design-system-aligned styling.

Form Integration

Implements the Form-Associated Custom Elements API — the slider's value is included in FormData when the form is submitted under its name.

Keyboard

Native range input handles all keyboard interactions:

  • Arrow keys: increment / decrement by step
  • PageUp / PageDown: larger jumps (browser-defined)
  • Home / End: jump to min / max

Gradient / custom track

Use the track slot to overlay custom content (e.g., a color gradient for hue/chroma/lightness sliders). Hide the solid fill via its color hook:

css
hy-slider.rainbow {
  --hy-slider-fill-color: transparent;
}

Examples

Basic usage

html
<hy-slider label="Spacing step" min="1" max="8" value="4"></hy-slider>

With hint, suffix, and variant

html
<hy-slider
  label="Motion speed"
  hint="0 = instant, 1 = languid"
  min="0"
  max="1"
  step="0.01"
  value="0.5"
  suffix="s"
  variant="warning"
>
</hy-slider>

Color slider with gradient track

html
<hy-slider label="Hue" min="0" max="360" value="180" suffix="°">
  <div
    slot="track"
    style="background: linear-gradient(to right, hsl(0,80%,50%), hsl(60,80%,50%), hsl(120,80%,50%), hsl(180,80%,50%), hsl(240,80%,50%), hsl(300,80%,50%), hsl(360,80%,50%));"
  ></div>
</hy-slider>

Error state

html
<hy-slider label="Volume" error error-message="Value exceeds safe listening level"> </hy-slider>

Discrete slider with tick marks and min/max labels

Opt in to ticks when the slider snaps to sparse stops — without marks, step-snapping can feel like cursor lag. Add tick-labels to anchor the scale with min/max readouts below the track. tick-labels is a no-op without ticks.

html
<hy-slider label="Base radius" min="0" max="8" step="1" value="4" suffix="px" ticks tick-labels>
</hy-slider>

Sparse, designer-named tick marks (compass directions, t-shirt sizes, …)

Use tickMarks to mark specific stops by value with optional labels. The slider keeps its step granularity for fine-grained drag; tick marks are visual references. Set tickSnap if you want the slider to restrict to those values.

html
const slider = document.querySelector('hy-slider'); slider.tickMarks = [ { value: 0, label: 'T',
ariaLabel: 'Top' }, { value: 90, label: 'R', ariaLabel: 'Right' }, { value: 180, label: 'B',
ariaLabel: 'Bottom' }, { value: 270, label: 'L', ariaLabel: 'Left' }, ];
html
<hy-slider label="Light angle" min="0" max="360" step="1"></hy-slider>

Snap-to-tickMarks slider (discrete picker disguised as a slider)

html
const slider = document.querySelector('hy-slider'); slider.tickMarks = [ { value: 0, label: 'S' }, {
value: 1, label: 'M' }, { value: 2, label: 'L' }, { value: 3, label: 'XL' }, ];
html
<hy-slider label="Size" min="0" max="3" tick-snap></hy-slider>

Fractional step with auto-inferred precision

Without any precision hint, 0.357 dragged through float math can render with trailing noise like 0.357…04. By default the component rounds the displayed value to the decimal count implied by step — step="0.01" → 2 decimals.

html
<hy-slider label="Chroma" min="0" max="1" step="0.01" value="0.357"></hy-slider>
<!-- readout: 0.36 -->

Explicit display precision

Override the auto-inferred precision — useful when step is coarser than the precision you want to show (e.g. step=1 but you want one decimal).

html
<hy-slider label="Zoom" step="1" value="42" display-precision="1"></hy-slider>
<!-- readout: 42.0 -->

Custom formatter — percentage

When you need to reshape the number entirely (scale, locale, custom units) pass formatValue as a property binding. When set, the formatter owns the full string — the suffix prop is NOT appended.

css
const slider = document.querySelector('hy-slider');
slider.formatValue = (v) => `${(v * 100).toFixed(1)}%`;
slider.value = 0.357; // readout: "35.7%"

Inline layout (dense parameter panels)

Label + hint sit on the left of the row, slider in the middle, value on the right — one row per parameter. Set --hy-slider-label-column-width on a wrapping element so stacked sliders align.

html
<div style="--hy-slider-label-column-width: 6rem;">
  <hy-slider
    layout="inline"
    label="Hue"
    hint="color angle"
    min="0"
    max="360"
    value="208"
    suffix="°"
  ></hy-slider>
  <hy-slider
    layout="inline"
    label="Chroma"
    hint="saturation"
    min="0"
    max="1"
    step="0.01"
    value="0.65"
  ></hy-slider>
  <hy-slider
    layout="inline"
    label="Vibrancy"
    hint="intensity"
    min="0"
    max="2"
    step="0.01"
    value="0.60"
  ></hy-slider>
</div>

Form submission

html
<form>
  <hy-slider name="brightness" min="0" max="100" value="75"></hy-slider>
  <button type="submit">Save</button>
</form>

Programmatic control

javascript
const slider = document.querySelector('hy-slider');
slider.stepUp(); // Advance by one step
slider.stepDown(5); // Retreat by five steps
slider.value = 42; // Set directly
slider.focus();

API

Properties

PropertyAttributeTypeDefaultDescription
valuevaluenumber0The current numeric value.
minminnumber0The minimum allowable value.
maxmaxnumber100The maximum allowable value.
stepstepnumber1The granularity between valid values.
disableddisabledbooleanfalseDisables the slider, preventing user interaction.
errorerrorbooleanfalseWhether the slider is in an error state.
namenamestring''The name attribute for form submission.
labellabelstring''The primary label displayed above the slider.
hinthintstring''Secondary hint text displayed under the label.
helperTexthelper-textstring''Helper text displayed below the slider.
errorMessageerror-messagestring''Error message shown when error is true (auto-sets error).
suffixsuffixstring''String appended to the value readout (e.g. "px", "%", "ms").
sizesize'small' | 'medium' | 'large''medium'Size variant affecting track and thumb dimensions.
variantvariant'default' | 'success' | 'warning' | 'danger''default'Visual variant affecting fill color.
layoutlayout'block' | 'inline''block'Row layout: block (label stacked above, default) or inline (label on the left in the same row as the slider). Use inline for dense parameter panels where vertical space is at a premium.
hideValuehide-valuebooleanfalseHides the numeric value readout to the right of the slider.
ticksticksbooleanfalseRenders a tick mark on the track at each step position. Useful for discrete sliders with sparse stops so the thumb's snapping behaviour feels intentional rather than laggy. Skipped when the step count would exceed 50.
tickLabelstick-labelsbooleanfalseShows the min and max values as labels under the track endpoints. Requires ticks to be set — labels without tick marks would anchor the scale without communicating where the snap positions are. Ignored when tickMarks is non-empty (sparse per-tick labels are the source of truth in that mode).
tickMarksTickMark[][]Sparse, designer-defined tick marks at chosen stops along the slider. Each entry has a value (positioned at ((value - min) / (max - min)) * 100% along the track), an optional label rendered below the track, and an optional ariaLabel. Out-of-range entries are silently dropped. Labels at min/max are auto-clamped (left-align at min, right-align at max) so they never bleed off the track edges.
tickSnaptick-snapbooleanfalseWhen true, restrict the slider's value to the entries declared in tickMarks, snapping to the nearest tick during drag and ignoring step for snap purposes. Defaults to false (tick marks are visual references only; drag granularity stays driven by step).
displayPrecisiondisplay-precisionnumber | undefinedNumber of decimal places shown in the value readout, tick labels, and aria-valuetext. Leave unset to auto-infer from step (step=0.01 → 2, step=0.1 → 1, step=1 → 0). Overridden by formatValue when set.
formatValue(value: number) => string | undefinedCustom formatter for the value readout / tick labels / aria-valuetext. When set, owns the full display string — suffix is NOT appended and displayPrecision is ignored. Must be a function, so it cannot be set via an HTML attribute; assign it as a property (el.formatValue = fn) or via a framework property binding (.formatValue=${fn} in Lit, formatValue={fn} in React).
inputIdinput-idstring''Explicit ID for the underlying <input> — auto-generated if empty.
autofocusautofocusbooleanfalseMarks this slider as the auto-focus target. Read by hy-dialog (and other overlay containers) on show to choose initial focus. Also focuses on first connect when used outside an overlay, mirroring native HTML autofocus but routed through JS so SSR'd HTML doesn't pull focus during hydration.
formHTMLFormElement | nullReturns the form element that contains this slider, if any.
validityValidityStateReturns the ValidityState object for the slider.
validationMessagestringReturns the validation message for the slider.

Events

EventDetailDescription
inputFired during drag / keyboard adjustment. Detail:
changeFired when the value is committed (pointer release / after key). Detail:

Slots

SlotDescription
trackCustom content inside the track, behind the fill (e.g. a gradient overlay).

CSS Parts

PartDescription
baseThe slider's outer wrapper
labelThe label container (label + hint)
rowRow containing the slider and value readout
inputThe native range input (visually hidden, interaction-only)
trackThe background rail
fillThe colored progress indicator
thumbThe draggable handle
ticksContainer for tick marks on the track (only present when ticks)
tickIndividual tick mark (only present when ticks)
tick-labelsContainer for min/max labels under the track (only present when both ticks and tick-labels are set, and tickMarks is empty)
tick-marksContainer for sparse tickMarks lines overlaid on the track (only present when tickMarks has at least one in-range entry)
tick-markIndividual sparse tick line (vertical, cuts through the track)
tick-mark-labelsContainer for labels under the track derived from tickMarks[].label
tick-mark-labelIndividual label below a sparse tick; carries .tick-mark-label--start|center|end modifier classes for endpoint auto-clamping
valueThe numeric value readout
helper-textThe helper text element
error-messageThe error message element

CSS Custom Properties

PropertyDescription
--hy-slider-height-smOuter hit-area height for small size variant. Per-component override in the form-control height contract (see .claude/rules/control-heights.md). Track and thumb stay smaller inside. Default: var(--hy-control-height-sm)
--hy-slider-height-mdOuter hit-area height for medium size variant. Default: var(--hy-control-height-md)
--hy-slider-height-lgOuter hit-area height for large size variant. Default: var(--hy-control-height-lg)
--hy-slider-track-heightTrack thickness (inner visual element)
--hy-slider-ornamentThumb diameter (inner visual element)
--hy-slider-thumb-colorThumb background color
--hy-slider-thumb-shadowThumb resting shadow
--hy-slider-thumb-shadow-hoverThumb hover shadow
--hy-slider-track-colorTrack background color
--hy-slider-fill-colorFill (progress) color
--hy-slider-radiusTrack and thumb border radius
--hy-slider-gapGap between label, slider, and helper text
--hy-slider-value-gapGap between slider and value readout
--hy-slider-value-widthMinimum width of the value readout column
--hy-slider-label-column-widthWidth of the label column in layout="inline" mode (no default — when unset, the label auto-sizes to its content). Set on a wrapping container so stacked sliders align.
--hy-slider-label-colorLabel text color
--hy-slider-hint-colorHint text color
--hy-slider-value-colorValue readout color
--hy-slider-helper-colorHelper text color
--hy-slider-danger-colorError text color
--hy-slider-tick-colorColor of ticks: boolean tick marks (default: surface base, so ticks cut through track + fill)
--hy-slider-tick-sizeWidth of each ticks: boolean tick mark
--hy-slider-tick-mark-colorColor of sparse tickMarks lines. Defaults to --hy-slider-tick-color so the two tick families look consistent when both are enabled
--hy-slider-tick-mark-label-colorText color of tickMarks labels under the track. Defaults to --hy-foreground-subtle-rest
--hy-slider-hover-durationTransition duration for hover and focus state feedback
--hy-slider-hover-easingTransition easing for hover and focus state feedback

Methods

checkValidity()

Checks if the slider's current value satisfies its constraints.

reportValidity()

Checks validity and reports any errors to the user.

focus()

Focuses the underlying range input.

blur()

Removes focus from the underlying range input.

stepUp()

Increments the value by n steps (defaults to 1). Clamps to max. Dispatches input and change events. No-op if disabled.

stepDown()

Decrements the value by n steps (defaults to 1). Clamps to min. Dispatches input and change events. No-op if disabled.

Built with Lit. Documented with VitePress.