Dual Range Slider
Min and max range with two draggable thumbs that cannot cross, a stretching selected segment, and value bubbles that rise and count while you drag.
About This Component
A two-handled range for filtering between a lower and an upper bound. Take hold of either thumb and it snaps through the step as you move, the selected segment of the track stretching to follow, while a bubble above the thumb lifts clear of your finger and counts to the number rather than flicking to it. The thumbs refuse to cross: each press narrows that thumb's bounds to wherever the other one is standing.
Read the full effect overview
Two real range inputs stay in the DOM behind the drawn control, so the pair submits with an ordinary form, reads correctly to a screen reader, and moves with the arrow keys. GSAP owns each thumb's x, which means no CSS transform anywhere on them. Ships in three looks, vault, soft and corporate, switched with one data-variant attribute.
What's Included
- Two Draggable thumbs on one track, bounded so they cannot cross
- Live snapping to the step declared on the native inputs
- Selected segment stretches between the thumbs as they move
- Value bubbles lift clear of the finger while dragging
- Bubble numbers count to the value through a tweened proxy
- Two hidden native range inputs keep the values for forms and the keyboard
- Arrow keys move a thumb with a back.out settle and a focus ring
- Plain stacked native sliders when JavaScript does not run
- Three shipped variants: vault, soft, corporate
Perfect For
- Price and budget filters on shop and marketplace listings
- Date, distance and duration windows on search interfaces
- Numeric bounds in dashboard and report filter panels
- Any pair of values where the gap between them is the answer
How It Works
The inputs hold the values
Two native range inputs supply the min, max and step, keep the values for form submission, and provide the keyboard path. Dragging writes into them and dispatches input and change behind a guard flag, so nothing loops and any listener you already have on the form keeps firing.
Bounds that narrow on press
Each thumb is a Draggable of type x with numeric bounds. On press, applyBounds is called with the other thumb's current position as the limit, so the pair can meet but never swap. liveSnap rounds x to whole steps in pixels, and the release tween settles exactly on the snapped value.
Bubbles that count
Each bubble's number is driven by a proxy object tweened with gsap.to, so a jump of several steps reads as counting rather than a flick. The bubble lifts on press with back.out and returns with power2.out, keeping the number clear of the finger holding the thumb.