Slider
Slider lets someone pick a single numeric value out of a bounded range by dragging a thumb along a track, such as a volume level, a brightness setting, or a price ceiling. It builds on a real native range field restyled to match the rest of the library, so dragging and every keyboard interaction come from the browser itself rather than a hand-built control.
Dependencies
When to use
Use a slider whenever someone is picking one numeric value out of a
bounded, continuous-feeling range where the relative position matters as
much as the exact number, such as volume, brightness, a price ceiling, or
a percentage. If the value needs to be typed precisely more often than
dragged, or there's no natural minimum and maximum to anchor a track to, a
Install & usage
Pick a framework in the toolbar above and these snippets adapt.
npm install @eunomia/elements
import "@eunomia/elements/slider.js";
<eun-slider name="volume" label="Volume" value="40"></eun-slider>
Importing the file registers <eun-slider> as a custom element, with no
further setup needed. It works with any framework, or none, since it's a
standard web component.
npm install @eunomia/elements
<script type="module">
import "@eunomia/elements/slider.js";
</script>
<eun-slider name="volume" label="Volume" value="40"></eun-slider>
npm install @eunomia/elements
import "@eunomia/elements/slider.js";
function VolumeField() {
return <eun-slider name="volume" label="Volume" value={40} />;
}
npm install @eunomia/elements
"use client";
import "@eunomia/elements/slider.js";
export function VolumeField() {
return <eun-slider name="volume" label="Volume" value={40} />;
}
npm install @eunomia/elements
<script setup>
import "@eunomia/elements/slider.js";
</script>
<template>
<eun-slider name="volume" label="Volume" value="40" />
</template>
npm install @eunomia/elements
import { CUSTOM_ELEMENTS_SCHEMA, Component } from "@angular/core";
import "@eunomia/elements/slider.js";
@Component({
selector: "app-volume-field",
template: `<eun-slider name="volume" label="Volume" value="40"></eun-slider>`,
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class VolumeFieldComponent {}
Alternatives
eun-countereun-inputGuidance
- Always set
label(or slot equivalent content), since a slider with no accessible name is a critical screen-reader failure (see the Accessibility tab) - Set
min/maxto a range that's actually meaningful for the value being picked, not the defaults (0–100) left unexamined - Use
graduatedwith a deliberatetick-stepwhen a few reference points genuinely help (e.g. every 25%), not one tick per single-unitstep
- Relying on the floating tooltip alone to convey the current value, since it's purely decorative (
aria-hidden) and the accessible value comes from the native slider itself - Using a slider for a value that's just as easily, and more precisely, typed, since that's what
or a numericcounter is forinput - Overriding colors with inline styles instead of the
--slider-*CSS variables
Live testing
Properties
Slider <eun-slider>
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| default-value | number | — | The default value applied when the field connects, and restored on form reset |
| hide-error | boolean | false | Whether the errors are hidden |
| hide-tooltip | boolean | false | Hides the floating value bubble that otherwise appears above the thumb while dragging or focused |
| tick-step | number | — | The spacing between graduation marks while graduated is set. Defaults to step itself |
| min | number | 0 | The minimum value allowed |
| max | number | 100 | The maximum value allowed |
| step | number | 1 | The granularity the value must follow, applied natively by the browser while dragging or using the keyboard |
| graduated | boolean | false | Draws decorative graduation marks along the track, spaced by tickStep, defaulting to step |
| value | number | — | The current numeric value |
Import the exact TypeScript type behind any property above, see
Properties
JS-only — no matching HTML attribute, set these from a script or a template binding.
| Name | Type | Default | Description |
|---|---|---|---|
| label | string | — | The title label of the field, rendered through the label component |
| instructions | string | — | Instructions displayed below the label |
| name | string | — | The field name |
| disabled | boolean | false | Whether the field is disabled |
| readonly | boolean | false | Whether the field is read only |
| required | boolean | false | Present for API consistency with every other field, but inert here, since a slider's value can never be empty the way a text field's can |
| hideError | boolean | false | Whether the errors are hidden |
| validators | Array<Validators<number>> | — | The list of validation rules applied to the field value |
Slots
| Name | Description |
|---|---|
| label | Custom label content, falling back to the label property |
| instructions | Custom instructions content, falling back to the instructions property |
Events
| Name | Type | Description |
|---|---|---|
| eunchange | ChangeEvent | Fired on every value change, live while dragging or typing |
Every event above follows the same naming convention, covered in
CSS custom properties
| Name | Description |
|---|---|
| --slider-track-height | Sets the height of the track |
| --slider-track-background | Sets the background color of the unfilled portion of the track |
| --slider-track-fill-background | Sets the background color of the filled portion of the track, from the minimum edge up to the thumb |
| --slider-track-border-radius | Sets the corner radius of the track |
| --slider-thumb-size | Sets the width and height of the thumb |
| --slider-thumb-background | Sets the color of the thumb |
| --slider-thumb-border-width | Sets the width of the border around the thumb |
| --slider-thumb-border-color | Sets the border color around the thumb, defaulting to the current fill color |
| --slider-thumb-shadow | Sets the shadow under the thumb |
| --slider-tick-size | Sets the width and height of each graduation mark |
| --slider-tick-color | Sets the color of graduation marks past the current value |
| --slider-tick-color-active | Sets the color of graduation marks at or before the current value, defaulting to the current fill color |
| --slider-tooltip-background | Sets the background color of the floating value bubble |
| --slider-tooltip-color | Sets the text color of the floating value bubble |
| --slider-error-color | Sets the color of the error message |
| --slider-hint-color | Sets the color of the hint message |
| --slider-focus-outline-color | Sets the color of the focus outline drawn around the thumb |
Basic
Leaving min and max unset defaults to 0–100, while leaving value
unset resolves to the midpoint of whatever range is in effect (50 here),
exactly like a bare native <input type="range"> would.
<eun-slider name="volume" label="Volume"></eun-slider>
Min, max and step
<eun-slider
name="temperature"
label="Target temperature (°C)"
min="16"
max="28"
step="0.5"
value="21"
></eun-slider>
Graduated
graduated draws a row of decorative marks along the track. tick-step
controls their spacing independently from step, so set it explicitly
whenever step is too fine to also double as a sensible tick spacing (a
step="1" slider over 0–100 shouldn't draw 101 marks).
<eun-slider
name="quality"
label="Quality"
min="0"
max="100"
step="1"
tick-step="25"
graduated
value="50"
></eun-slider>
Tooltip
By default, a floating bubble mirrors the current value above the thumb
while dragging or focused. Set hide-tooltip to turn it off entirely.
<eun-slider
name="brightness"
label="Default, drag or tab to see it"
value="65"
></eun-slider>
<eun-slider
name="contrast"
label="hide-tooltip"
hide-tooltip
value="65"
></eun-slider>
States
Disabled
Removed from the tab order and excluded from form submission entirely.
<eun-slider name="volume" label="Disabled" value="30" disabled></eun-slider>
Readonly
Stays focusable and its value still submits, but it can't be dragged or
changed by keyboard, since this needs more than the plain HTML readonly
attribute on a range input (see the Accessibility tab for why).
<eun-slider name="volume" label="Readonly" value="70" readonly></eun-slider>
Hint and error
<eun-slider
name="budget"
label="Monthly budget ceiling"
hint="Applies from the next billing cycle"
min="0"
max="2000"
step="50"
value="800"
></eun-slider>
Custom validators still apply normally, though a slider's own required
has no effect since its value can never be "empty" (see the Accessibility
tab). A business-rule validator still works exactly like on any other
field:
<eun-slider
name="seats"
label="Seats to allocate"
min="0"
max="20"
value="0"
></eun-slider>
document.querySelector("eun-slider").validators = [
{
isValid: (value: number) => value > 0,
message: "Allocate at least one seat",
},
];
Forms
Like every other Eunomia field, eun-slider is a real
<form>'s
submission and reset.
<form id="my-form">
<eun-slider name="volume" label="Volume" default-value="40"></eun-slider>
<eun-slider
name="brightness"
label="Brightness"
default-value="70"
></eun-slider>
<button type="submit">Submit</button>
<button type="reset">Reset</button>
</form>
const form = document.querySelector("#my-form");
// Fires live while dragging, exactly like the native `input` event it's
// driven by.
form.addEventListener("eunchange", () => {
console.log(Object.fromEntries(new FormData(form)));
});
// Fires on reset (native form event), not on eunchange ; `setTimeout` lets
// the sliders' own reactive update settle first.
form.addEventListener("reset", () => {
setTimeout(() => console.log(Object.fromEntries(new FormData(form))));
});
Custom
Override the --slider-* CSS variables, listed in full in the API tab.
<eun-slider
class="custom-slider"
label="Custom slider"
value="60"
graduated
tick-step="20"
></eun-slider>
.custom-slider {
--slider-track-fill-background: #be185d;
--slider-thumb-border-color: #be185d;
--slider-tick-color-active: #be185d;
--slider-tooltip-background: #be185d;
--slider-thumb-size: 22px;
}
Keyboard interactions
All native, from the underlying <input type="range">, meaning nothing
here is a custom reimplementation, except blocking every key below while
readonly (see "Disabled vs. readonly" further down).
| Key | Action |
|---|---|
Tab / Shift+Tab |
Moves focus in/out of the field, natively (single tab stop) |
ArrowRight / ArrowUp |
Increases the value by step, natively, clamped to max |
ArrowLeft / ArrowDown |
Decreases the value by step, natively, clamped to min |
Home |
Jumps to min, natively |
End |
Jumps to max, natively |
Page Up / Page Down |
Jumps by a browser-defined larger step, natively |
Aria attributes
The native field is a real <input type="range">, so its implicit
role="slider" and its aria-valuenow/aria-valuemin/aria-valuemax are
exposed by the browser itself from value/min/max, since nothing here
sets them manually. On top of that native baseline:
aria-invalid="true"|"false": kept in sync withvalid(driven by any customvalidators, and see "Required field" below for why native range/step constraints never factor in here)aria-label: set from thelabelproperty directly on the native field, for the same cross-shadow-boundary reason documented oneun-counter's own Accessibility tab (aria-labelledby/fordon't cross shadow-root boundaries, so a plain-textaria-labelis used instead of pointing at the rendered<eun-label>)aria-describedby="description": set on the field whenever a hint or error message is actually rendered below itaria-readonly: reflected on the host element itself whilereadonly(there's no nativearia-readonlyequivalent the field sets on its own, since HTMLreadonlydoesn't apply totype="range"at all, as explained below)
Label accessibility
Always set label (or slot equivalent content into the label slot),
since a slider with no accessible name is one of the most disruptive
screen-reader failures. State the unit where it isn't obvious from context
(e.g. "Target temperature (°C)" rather than a bare "Temperature").
Disabled vs. readonly
disabled sets the native disabled attribute on the field directly: it
removes the slider from the tab order, blocks every interaction, and
excludes its value from form submission entirely, which is all standard
native behavior, not anything custom.
readonly needed custom handling, because the HTML readonly attribute
has no effect on <input type="range"> at all, since per the HTML
specification, readonly only applies to text-like input types (text,
number, date, ...), never to range (or checkbox/radio, for the
same reason eun-switch doesn't rely on it either). eun-slider reproduces
the intent of readonly instead: pointer interaction is blocked with
pointer-events: none in CSS, and every keyboard interaction is blocked in
script (keydown is intercepted and prevented while readonly). The field
stays focusable and its value stays visible, selectable, and submitted, so
it communicates "you can't change this right now", not "this doesn't
apply". Prefer readonly over disabled whenever the value is still
meaningful context for the user.
Required field
required is present on eun-slider for API consistency with every other
field, but it's inert here: the native valueMissing check it drives
elsewhere only ever fires against a genuinely empty value, and a slider's
value can never be empty the way a text field's can, since connecting
the component with no value/default-value set resolves it to a
definite number immediately (the midpoint of min/max, exactly like a
bare <input type="range"> would on its own). Use a custom validators
entry instead for any "this specific value isn't acceptable" rule (see the
Examples tab's "Hint and error" section).
Native range/step constraints (rangeUnderflow, rangeOverflow,
stepMismatch) never surface either, for a related reason: per spec, a
range input's own value sanitization algorithm silently clamps
out-of-bounds or off-step values instead of flagging them invalid. That's
unlike <input type="number">, which is what eun-counter relies on for
its own constraint validation. eun-slider's value property mirrors
that same clamping.
Tooltip and graduation marks
Both the floating value tooltip and the graduation marks are purely
decorative, aria-hidden throughout: the accessible value comes from the
native slider's own implicit aria-valuenow (and its live, continuous
announcement while dragging with a keyboard), not from either of these.
Never rely on the tooltip alone to convey the current value to someone
using a screen reader.
Reference links