HySlider
Usage examples on this page are written for Lit / plain HTML (
<hy-slider>). The same component ships asHySliderin@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:
hy-slider.rainbow {
--hy-slider-fill-color: transparent;
}Examples
Basic usage
<hy-slider label="Spacing step" min="1" max="8" value="4"></hy-slider>With hint, suffix, and variant
<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
<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
<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.
<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.
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' }, ];<hy-slider label="Light angle" min="0" max="360" step="1"></hy-slider>Snap-to-tickMarks slider (discrete picker disguised as a slider)
const slider = document.querySelector('hy-slider'); slider.tickMarks = [ { value: 0, label: 'S' }, {
value: 1, label: 'M' }, { value: 2, label: 'L' }, { value: 3, label: 'XL' }, ];<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.
<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).
<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.
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.
<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
<form>
<hy-slider name="brightness" min="0" max="100" value="75"></hy-slider>
<button type="submit">Save</button>
</form>Programmatic control
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
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
value | value | number | 0 | The current numeric value. |
min | min | number | 0 | The minimum allowable value. |
max | max | number | 100 | The maximum allowable value. |
step | step | number | 1 | The granularity between valid values. |
disabled | disabled | boolean | false | Disables the slider, preventing user interaction. |
error | error | boolean | false | Whether the slider is in an error state. |
name | name | string | '' | The name attribute for form submission. |
label | label | string | '' | The primary label displayed above the slider. |
hint | hint | string | '' | Secondary hint text displayed under the label. |
helperText | helper-text | string | '' | Helper text displayed below the slider. |
errorMessage | error-message | string | '' | Error message shown when error is true (auto-sets error). |
suffix | suffix | string | '' | String appended to the value readout (e.g. "px", "%", "ms"). |
size | size | 'small' | 'medium' | 'large' | 'medium' | Size variant affecting track and thumb dimensions. |
variant | variant | 'default' | 'success' | 'warning' | 'danger' | 'default' | Visual variant affecting fill color. |
layout | layout | '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. |
hideValue | hide-value | boolean | false | Hides the numeric value readout to the right of the slider. |
ticks | ticks | boolean | false | Renders 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. |
tickLabels | tick-labels | boolean | false | Shows 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. |
tickSnap | tick-snap | boolean | false | When 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). |
displayPrecision | display-precision | number | 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). |
inputId | input-id | string | '' | Explicit ID for the underlying <input> — auto-generated if empty. |
autofocus | autofocus | boolean | false | Marks 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
| Event | Detail | Description |
|---|---|---|
input | — | Fired during drag / keyboard adjustment. Detail: |
change | — | Fired when the value is committed (pointer release / after key). Detail: |
Slots
| Slot | Description |
|---|---|
track | Custom content inside the track, behind the fill (e.g. a gradient overlay). |
CSS Parts
| Part | Description |
|---|---|
base | The slider's outer wrapper |
label | The label container (label + hint) |
row | Row containing the slider and value readout |
input | The native range input (visually hidden, interaction-only) |
track | The background rail |
fill | The colored progress indicator |
thumb | The draggable handle |
ticks | Container for tick marks on the track (only present when ticks) |
tick | Individual tick mark (only present when ticks) |
tick-labels | Container for min/max labels under the track (only present when both ticks and tick-labels are set, and tickMarks is empty) |
tick-marks | Container for sparse tickMarks lines overlaid on the track (only present when tickMarks has at least one in-range entry) |
tick-mark | Individual sparse tick line (vertical, cuts through the track) |
tick-mark-labels | Container for labels under the track derived from tickMarks[].label |
tick-mark-label | Individual label below a sparse tick; carries .tick-mark-label--start|center|end modifier classes for endpoint auto-clamping |
value | The numeric value readout |
helper-text | The helper text element |
error-message | The error message element |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-slider-height-sm | Outer 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-md | Outer hit-area height for medium size variant. Default: var(--hy-control-height-md) |
--hy-slider-height-lg | Outer hit-area height for large size variant. Default: var(--hy-control-height-lg) |
--hy-slider-track-height | Track thickness (inner visual element) |
--hy-slider-ornament | Thumb diameter (inner visual element) |
--hy-slider-thumb-color | Thumb background color |
--hy-slider-thumb-shadow | Thumb resting shadow |
--hy-slider-thumb-shadow-hover | Thumb hover shadow |
--hy-slider-track-color | Track background color |
--hy-slider-fill-color | Fill (progress) color |
--hy-slider-radius | Track and thumb border radius |
--hy-slider-gap | Gap between label, slider, and helper text |
--hy-slider-value-gap | 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-label-color | Label text color |
--hy-slider-hint-color | Hint text color |
--hy-slider-value-color | Value readout color |
--hy-slider-helper-color | Helper text color |
--hy-slider-danger-color | Error text color |
--hy-slider-tick-color | Color of ticks: boolean tick marks (default: surface base, so ticks cut through track + fill) |
--hy-slider-tick-size | Width of each ticks: boolean tick mark |
--hy-slider-tick-mark-color | Color 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-color | Text color of tickMarks labels under the track. Defaults to --hy-foreground-subtle-rest |
--hy-slider-hover-duration | Transition duration for hover and focus state feedback |
--hy-slider-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.
stepDown()
Decrements the value by n steps (defaults to 1). Clamps to min. Dispatches input and change events. No-op if disabled.