By Tim King · Updated
Dual Range Slider Builder
Two-Thumb Min and Max Slider for Price Filters, No Library Needed
OneTask is free, with no sign-up, and built by one person.
If this tool saved you time, a coffee helps keep new ones coming.
How the Dual Range Slider works
This dual range slider builder makes a two-thumb control from standard inputs. Price filters, date windows and "between" queries need two handles on one track. HTML has no dual range input, so this builder stacks two native sliders, paints the fill between them and adds a script that stops the handles crossing. Drag either thumb in the preview, then copy the code.
A dual range slider lets someone pick a span instead of a single value: a price band, a date
window, an age range or a salary bracket. HTML has no native two-handle slider, so this
generator builds one from two ordinary <input type="range"> elements stacked on one track,
plus a few lines of CSS and script. You get the look you set on the left and copy-ready code
that works without a framework.
Why it matters. Range filters are everywhere in e-commerce, property search, travel and job boards. Baymard Institute's usability testing of e-commerce filtering found that sliders alone frustrate users who want an exact number, and it recommends pairing them with text fields. A two-handle slider built on native inputs keeps the keyboard support, form submission and accessibility the browser already provides, which most JavaScript slider libraries have to recreate by hand.
How the dual range slider works
- Two inputs, one track. Both inputs are positioned absolutely on top of each other.
Each keeps its own value and keyboard behaviour, and both submit with the form as
minandmax, so a plain GET form produces?min=200&max=750with no script at all. - Clicks reach the right thumb. With two inputs stacked, the upper one would swallow
every pointer event. The CSS sets
pointer-events: noneon both inputs andpointer-events: autoon their thumbs only. Clicking the bare track does nothing, which is deliberate: on a two-handle slider a track click is ambiguous about which handle to move. - Track and fill. The inputs' own tracks are transparent. The grey track is a
::beforeon the container, and the coloured fill is a<div>positioned by two custom properties,--loand--hi, holding each thumb's position as a percentage. The script updates only those two properties. - Thumbs never cross. On every
inputevent the script checks whether the lower value has passed the upper value minus the minimum gap. If it has, the handle being dragged is pushed back, so the thumbs stop at the gap. A gap of zero lets the two values meet. - Live readout. An
<output>witharia-live="polite"shows the range, such as "£200 – £750", using your value prefix, and screen readers announce it when it changes.
Reading the preview. The preview is the real component, not a picture of it. Drag each thumb, then Tab to a thumb and use the arrow keys, Page Up and Page Down, Home and End. The code panel always matches what you see, so you can test an edge case, such as a large step or a wide gap, before copying.
Choosing the settings
- Min, max and step define the scale. Choose a step that matches how people think about the value: 10 or 50 for prices in the hundreds, 1 for ages, 1,000 for salaries. Too many positions on a short track makes single values impossible to hit with a mouse.
- Lower and upper value set the starting range. Start wide, usually the full min and max, so users narrow the results rather than wonder why some are missing.
- Minimum gap stops the two values meeting. For a price filter a gap of one step is usually right; for a booking window it might be the shortest allowed stay.
- Thumb size matters on touch screens. WCAG 2.2 sets 24 by 24 CSS pixels as the minimum target size, and Apple's guidelines ask for 44 points. The default is comfortable on a phone; go smaller only for desktop-only dashboards.
- Colours. The fill should contrast with the track by at least 3:1, the WCAG level for meaningful graphics, or the selected span disappears for users with low vision.
Tips and pitfalls.
- Pair it with number inputs when exact values matter. Sliders are fast for "roughly here" and slow for "exactly £437".
- Firefox thumb borders. Firefox draws the thumb border outside the declared width, so the generated Firefox rule subtracts twice the border to keep handles the same size everywhere. Keep that rule if you edit the CSS.
- Accessible names. Each input has
aria-labelset to Minimum or Maximum andaria-labelledbypointing at the visible label. Becausearia-labelledbywins when both are present, test with a screen reader. If both handles are announced only by the label, add each input's own label element to itsaria-labelledbylist. - Focus visibility. Focus rings are drawn on the thumbs with
:focus-visible, so Tab shows which handle is active. Do not remove them to tidy the look. - Server-side checks. The script keeps min below max in the browser, but a hand-edited URL
can still send
min=900&max=100. Validate on the server.
For a single-value control, use the range slider generator. To show progress through a longer form, try the form progress bar.
FAQ
Why not use a library like noUiSlider?
Libraries add tooltips, pips, non-linear scales and touch refinements. If you need those they are worth the dependency. For a plain min/max filter, two native inputs give you keyboard and screen reader support for free with under thirty lines of script, and the values submit with the form.
Can I make the fill draggable to move both thumbs together?
Yes, but it needs pointer capture and some maths: on pointerdown over the fill, record the offset, then on pointermove shift both values by the same delta while respecting the ends. It is a nice touch for wide ranges but not something users expect, so it is left out here.
How do I show the values in bubbles above each thumb?
Position two spans absolutely over the track with left: var(--lo) and left: var(--hi), shifted
by translateX(-50%). The custom properties already track the thumbs, so the bubbles follow with
no extra script beyond setting their text.
Why does the track click do nothing?
Because both inputs ignore pointer events except on their thumbs. If you want track clicks to move
the nearest thumb, add a pointerdown listener on the container, compute the clicked percentage,
and set whichever input's value is closer. It is a few lines but an easy source of surprises.
Does this work with dates or non-numeric values?
Keep the inputs numeric and map. For dates, use days since a start date as the value and format
the readout with Date. For a list of named steps, use the index as the value and look up the
name. The slider only ever deals in numbers.
See also: all Forms & Inputs tools, Layout & Spacing, Shadows & Motion, Shapes & SVG, UI Components, Colour.
Related tools
Range Slider Builder
Custom-styled range input with filled track and live value.
Form Progress Bar
Step indicators and progress bars for multi-step forms and checkouts.
Custom Radio Button Builder
Accessible custom radio buttons as dots, cards or pill segments.
Floating Label Form
Material-style floating labels in pure CSS with outlined, filled and underline variants.
Input Mask Generator
Auto-formatting inputs for phone, date, card and custom patterns.