
A touch-friendly, fully accessible, slim-style range slider Custom Element written in JavaScript.
How to use it:
1. Install and import the range-slider-element
# NPM $ npm i range-slider-element
import 'range-slider-element'; // OR <script type="module" src="https://unpkg.com/range-slider-element@latest/dist/range-slider-element.js"></script>
2. Add the range-slider to the page. Available props:
- min: Min value
- max: Max value
- value: Current value
- step: Step size
- dir: “ltr” or “rtl”
- orientation: “vertical” for vertical range sliders
<range-slider min="0" max="100">
<div data-track></div>
<div data-track-fill></div>
<div data-runnable-track>
<div data-thumb>
<output class="value-bubble"></output>
</div>
</div>
</range-slider>3. Customize the range slider.
range-slider {
--track-size: 0.4rem;
--thumb-size: 1.4rem;
}
range-slider:not([orientation="vertical"]) {
inline-size: 100%;
}
range-slider [data-thumb]:has(.value-bubble) {
display: flex;
align-items: flex-end;
justify-content: center;
transition: transform 200ms ease;
will-change: transform;
}
range-slider [data-thumb] .value-bubble {
--width: 30px;
--height: 60px;
box-sizing: border-box;
position: absolute;
padding-block-start: 0.8em;
margin-block-end: 0.5em;
width: var(--width);
height: var(--height);
background-image: url('data:image/svg+xml,');
background-repeat: no-repeat;
background-position: center;
background-size: var(--width) var(--height);
color: white;
text-align: center;
font-size: 0.7em;
font-weight: 400;
text-shadow: 0 -0.5px 0 rgba(0, 0, 0, 0.4);
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));
transition: opacity 200ms ease;
user-select: none;
pointer-events: none;
}
range-slider [data-thumb]:has(.value-bubble):active {
transform: scale(1.5);
}
range-slider [data-thumb]:has(.value-bubble):active .value-bubble {
opacity: 1;
}4. Event handlers.
const valueBubble = document.querySelector('.value-bubble');
const valueBubbleRangeElement = valueBubble.closest('range-slider');
const updateValue = event => valueBubble.textContent = event.target.value;
// Set initial output value
valueBubble.textContent = valueBubbleRangeElement.value;
// Update output value
document.addEventListener('input', updateValue);
document.addEventListener('change', updateValue);5. The component also supports multiple thumbs since v2.
<range-slider min="10" max="90" value="10,40" name="price-range">
<div data-track></div>
<div data-track-fill></div>
<div data-runnable-track>
<div data-thumb aria-label="Minimum Price"></div>
<div data-thumb aria-label="Maximum Price"></div>
</div>
</range-slider>
<output>1</output>range-slider {
--track-size: .4rem;
--thumb-size: 1.4rem;
}
range-slider:not([orientation="vertical"]) {
inline-size: 100%;
}
range-slider [data-track-fill] {
background-color: mediumspringgreen;
}
range-slider [data-thumb] {
background-color: mediumspringgreen;
border: 2px solid white;
box-shadow: 0 0 0 1px rgb(0, 0, 0, 10%);
box-sizing: border-box;
transition: scale 200ms ease-in-out;
}
range-slider [data-thumb]:active {
scale: 1.4;
}
range-slider [data-thumb]:focus-visible {
outline: 2px solid springgreen;
}const valueOutput = document.querySelector('output');
const rangeSliderElement = document.querySelector('range-slider');
const formatValue = value => value.split(',').join(' - ');
const updateValue = event => valueOutput.textContent = formatValue(event.target.value);
// Set initial output value
valueOutput.textContent = formatValue(rangeSliderElement.value);
// Update output value
document.addEventListener('input', updateValue);
document.addEventListener('change', updateValue);Changelog:
v2.1.1 (09/22/2025)
- fix: html5 compliant step rounding to use min as step base
v2.1.0 (07/13/2025)
- feat: allow opt-out of define()
- feat(dx): introduce typescript types support
v2.0.0 (03/04/2025)
- Multi thumb support
- Vertical orientation support
- HTML form support
- Bugfixes
02/20/2025
- Bugfixes






