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

Layout ​

In block layout the label sits one form gap above the thumb: the row is a full control height tall (the hit area) with the track centered in it, so the row is pulled up by the transparent band above the thumb. In inline layout the row keeps its full height, lining up with other form controls.

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-background-brand-indicator-rest: transparent;
}

When to use

  • A value where the feel matters more than the exact number: volume, opacity, a zoom level.
  • A range between two bounds, with range: a price range, a time span.

Do

  • Leave the value shown unless a label at each end says enough.
  • Set step to the smallest change that makes a difference.

When not to use

  • An exact number, an open-ended range, or one so wide a pixel spans many values: hy-number-input.
  • A few named options: hy-radio-group, or hy-select when space is tight.
  • An angle or a direction: hy-angle-picker.
  • Showing a value people do not set: hy-meter for a measurement, hy-progress for progress.

Accessibility: your part ​

  • Choose a step that lets keyboard users reach every meaningful value in a few presses.

Examples ​

Basic usage ​

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

With hint and suffix ​

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

Color slider with gradient track ​

html
<style>
  .hue-track {
    background: linear-gradient(to right in hsl longer hue, hsl(0 80% 50%), hsl(360 80% 50%));
  }
</style>
<hy-slider label="Hue" min="0" max="360" value="180" suffix="°">
  <div slot="track" class="hue-track"></div>
</hy-slider>

Error state ​

html
<hy-slider label="Bitrate" max="320" value="320" error-message="Exceeds the license tier limit">
</hy-slider>

Warning state ​

The value is accepted but worth a second look: the fill and the message take the warning color, the message is announced politely, and the form still submits. Setting warning-message raises warning; error wins when both are set.

html
<hy-slider label="Volume" value="92" suffix="%" warning-message="Loud: may damage hearing">
</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.

javascript
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) ​

javascript
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 -->

Range — two thumbs for a filter ​

The thumbs never cross; each is its own tab stop, named by the label plus start-label / end-label ("Price, Minimum"). With name the form submits price.start and price.end.

html
<hy-slider
  label="Price"
  range
  min="0"
  max="500"
  step="10"
  value-start="50"
  value-end="250"
  suffix=" CHF"
  name="price"
></hy-slider>
<script>
  document.querySelector('hy-slider[range]').addEventListener('change', (e) => {
    console.log(e.detail.start, e.detail.end, e.detail.thumb);
  });
</script>

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.

javascript
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
<style>
  .parameters {
    --hy-slider-label-column-width: 6rem;
  }
</style>
<div class="parameters">
  <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.
warningwarningbooleanfalseWarning state: the value is accepted but worth a second look. Paints the fill and the message in the warning color; never sets aria-invalid and never blocks submission. error wins when both are set.
namenamestring''The name attribute for form submission.
labellabelstring''The primary label displayed above the slider.
localelocalestring | undefined—BCP-47 locale for the shown values' digits and separators. Unset, the nearest lang (across shadow roots), else the browser's language. The value and the form value stay plain numbers.
hinthintstring''Secondary hint text displayed under the label.
helperTexthelper-textstring''Helper text displayed below the slider.
errorMessageerror-messagestring''Error message shown under the slider (replaces the helper text). Setting it raises error; clearing it lowers it.
warningMessagewarning-messagestring''Warning message shown under the slider (replaces the helper text). Setting it raises warning; clearing it lowers it.
suffixsuffixstring''String appended to the value readout (e.g. "px", "%", "ms").
sizesize'small' | 'medium' | 'large''medium'Size variant affecting track and thumb dimensions.
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 behavior 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).
tickMarks—TickMark[][]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 | undefined—Number 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 | undefined—Custom 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).
rangerangebooleanfalseTwo thumbs on one track: the slider holds a range, value-start to value-end (price, date and size filters). The thumbs never cross — each stops at the other — and each is its own tab stop and role="slider", named by the label plus start-label / end-label. With name the form submits {name}.start and {name}.end (as hy-date-picker's range). value is ignored.
valueStartvalue-startnumber | undefined—Range mode: the lower thumb's value. Unset starts at min.
valueEndvalue-endnumber | undefined—Range mode: the upper thumb's value. Unset starts at max.
startLabelstart-labelstring'Minimum'Range mode: the lower thumb's name, announced after the label ("Price, Minimum"). Set it for other words ("From") or to localize.
endLabelend-labelstring'Maximum'Range mode: the upper thumb's name ("Price, Maximum").
inputIdinput-idstring''Explicit id for the underlying <input>; empty uses the fixed input (components.md "Ids").
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.
form—HTMLFormElement | null—Returns the form element that contains this slider, if any.
validity—ValidityState—Returns the ValidityState object for the slider.
validationMessage—string—Returns the validation message for the slider.

Events ​

EventDetailDescription
input—Fired during drag / keyboard adjustment. Detail: { value: number }; in range mode also start, end and thumb
change—Fired when the value is committed (pointer release / after key). Detail: { value: number }; in range mode also start, end and thumb

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 (both handles in range mode)
input-startRange mode: the lower thumb's native input
input-endRange mode: the upper thumb's native input
thumb-startRange mode: the lower handle
thumb-endRange mode: the upper 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
warning-messageThe warning message element
message-iconThe icon starting the error or warning message

CSS Custom Properties ​

The documented hooks. Every override hook the component reads — one per token it paints — is listed in @whitespaceux/harmony/theme-api.json, and @whitespaceux/harmony/component-bindings.json (the Harmony MCP get-bindings tool) says which part and state each one paints.

PropertyDefaultDescription
--hy-slider-control-height-smvar(--hy-control-height-sm)Outer hit-area height for small size variant. Per-component override in the form-control height contract. Track and thumb stay smaller inside.
--hy-slider-control-height-mdvar(--hy-control-height-md)Outer hit-area height for medium size variant.
--hy-slider-control-height-lgvar(--hy-control-height-lg)Outer hit-area height for large size variant.
--hy-slider-slider-track-height-mdvar(--hy-slider-track-height-md)Track thickness (inner visual element)
--hy-slider-slider-track-height-smvar(--hy-slider-track-height-sm)Track thickness (inner visual element)
--hy-slider-slider-track-height-lgvar(--hy-slider-track-height-lg)Track thickness (inner visual element)
--hy-slider-ornament-pelletvar(--hy-ornament-pellet)Thumb diameter (inner visual element)
--hy-slider-ornament-handlevar(--hy-ornament-handle)Thumb diameter (inner visual element)
--hy-slider-ornament-knobvar(--hy-ornament-knob)Thumb diameter (inner visual element)
--hy-slider-thumb-background-surface-basevar(--hy-background-surface-base)Thumb background color
--hy-slider-shadow-elevation-2var(--hy-shadow-elevation-2)Thumb resting shadow
--hy-slider-shadow-elevation-3var(--hy-shadow-elevation-3)Thumb hover shadow
--hy-slider-background-surface-mediumvar(--hy-background-surface-medium)Track background color
--hy-slider-background-brand-indicator-restvar(--hy-background-brand-indicator-rest)Fill (progress) color
--hy-slider-background-danger-indicator-restvar(--hy-background-danger-indicator-rest)Fill (progress) color
--hy-slider-background-warning-indicator-restvar(--hy-background-warning-indicator-rest)Fill (progress) color
--hy-slider-radius-pill-mdvar(--hy-radius-pill-md)Track and thumb border radius
--hy-slider-gap-form-smvar(--hy-gap-form-sm)Gap between label, slider, and helper text
--hy-slider-gap-form-mdvar(--hy-gap-form-md)Gap between slider and value readout
--hy-slider-value-width—Minimum width of the value readout column
--hy-slider-label-column-width—Width 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-foreground-default-restvar(--hy-foreground-default-rest)Label text color
--hy-slider-hint-foreground-subtle-restvar(--hy-foreground-subtle-rest)Hint text color
--hy-slider-value-foreground-subtle-restvar(--hy-foreground-subtle-rest)Value readout color
--hy-slider-helper-foreground-subtle-restvar(--hy-foreground-subtle-rest)Helper text color
--hy-slider-foreground-danger-restvar(--hy-foreground-danger-rest)Error message text and icon color
--hy-slider-foreground-warning-restvar(--hy-foreground-warning-rest)Warning message text and icon color
--hy-slider-gap-layout-2xsvar(--hy-gap-layout-2xs)Gap between the message icon and its text
--hy-slider-tick-background-surface-basevar(--hy-background-surface-base)Color of ticks: boolean tick marks (default: surface base, so ticks cut through track + fill)
--hy-slider-tick-stroke-mdvar(--hy-stroke-md)Width of each ticks: boolean tick mark
--hy-slider-tick-mark-background-surface-basevar(--hy-background-surface-base)Color of sparse tickMarks lines (the same surface color as the tick dots, so the two tick families look consistent when both are enabled)
--hy-slider-tick-mark-label-foreground-subtle-restvar(--hy-foreground-subtle-rest)Text color of tickMarks labels under the track.
--hy-slider-motion-transition-hover-durationvar(--hy-motion-transition-hover-duration)Transition duration for hover and focus state feedback
--hy-slider-motion-transition-hover-easingvar(--hy-motion-transition-hover-easing)Transition 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. In range mode thumb picks the thumb (default end), which stops at the other one.

stepDown() ​

Decrements the value by n steps (defaults to 1). Clamps to min. Dispatches input and change events. No-op if disabled. In range mode thumb picks the thumb (default end), which stops at the other one.

Built with Lit. Documented with VitePress.