“Build a range slider” is a frontend machine-coding question that punishes assumptions. The native <input type="range"> works for simple cases but fails for dual-thumb (price ranges), custom styling, marks, snap points, and complex accessibility. The interview tests pointer math, ARIA pattern, and the keyboard mechanics that screen-reader users rely on.
Clarify scope
- Single thumb or dual thumb (range)?
- Snap to discrete marks or continuous?
- Show labels or values?
- Vertical orientation in scope?
- Mobile UX (larger touch target)?
The architecture
- Track: a styled div with width 100%
- Filled portion: another div with absolute positioning, width based on value
- Thumb: a button absolutely positioned
- Hidden inputs for form integration (single or two)
Pointer interaction
pointerdownon a thumb (or anywhere on track for tap-to-jump)- Capture the pointer (
setPointerCapture) - Compute new value from pointer position relative to track
- Clamp to min/max; snap to step
- Update state; emit onChange
- On
pointerup: release capture, emit onCommit
Value-from-position math
const rect = trackRef.current.getBoundingClientRect();
const ratio = (event.clientX - rect.left) / rect.width;
const range = max - min;
const raw = min + ratio * range;
const stepped = Math.round((raw - min) / step) * step + min;
const clamped = Math.max(min, Math.min(max, stepped));
Dual thumb — the range variant
- Two values: min and max
- Each thumb constrained: low cannot exceed high (and vice versa)
- Filled portion is between the two thumbs
- Drag a thumb past the other: swap roles
- Track click: move the closer thumb
Keyboard interaction
- Tab focuses the thumb
- ArrowLeft / ArrowDown: decrease by step
- ArrowRight / ArrowUp: increase by step
- PageDown / PageUp: decrease / increase by larger amount
- Home / End: jump to min / max
- For dual thumb: each thumb is separately tabbable
Accessibility — WAI-ARIA slider pattern
- Each thumb has
role="slider" aria-valuemin,aria-valuemax,aria-valuenowaria-valuetextfor non-numeric values (“Wednesday”)aria-orientation="horizontal"(or vertical)- Each thumb has a label (
aria-labeloraria-labelledby) - For dual thumb: distinguish “minimum” and “maximum” labels
Snap and marks
- Step value rounds to discrete positions
- Visible marks: render small dots or ticks at step positions
- Labels above marks for select positions (e.g., $0, $250, $500)
- “Magnetic” snap: gravitate toward marks during drag
Touch considerations
- Touch target should be at least 44×44 px (WCAG)
- Visual thumb can be smaller; hit area can be larger via padding
- Show the value in a tooltip / bubble during drag for clarity
- Disable browser pinch / scroll on the track
Visual states
- Default: thumb visible, neutral color
- Hover: subtle scale or shadow
- Focus: visible focus ring (do not remove the default outline without replacement)
- Active (dragging): bigger thumb, value tooltip
- Disabled: muted; pointer cursor disabled
RTL handling
- Reverse the value-from-position math when dir=”rtl”
- Or use CSS logical properties + a flipped track
Complex inputs
For range with a histogram (Amazon-style price filter):
- Render histogram bars behind the track
- Bar height = count in that bucket
- Bars within selected range fully colored; outside dimmed
Edge cases interviewers love
- User drags past viewport edge — pointer capture handles it
- Step does not divide range evenly — round and clamp
- Min === max — disabled thumbs, do not allow drag
- Both thumbs at same position — should snap one off the other
- Mouse leaves window during drag — keep tracking via capture
Performance
- Use CSS transforms for thumb position, not
left - Throttle pointermove to rAF if doing expensive work
- Avoid React re-renders during drag if possible (manipulate DOM via ref)
What separates senior from staff
Senior implementations get the slider pointer math right. Staff implementations handle the dual-thumb correctly, the keyboard pattern fully, the WAI-ARIA semantics, and the touch hit-area. Principal candidates raise the histogram variant and the value-formatting strategy (currency, dates, etc.).
Frequently Asked Questions
Library options for production?
Radix Slider, react-aria Slider (Adobe), rc-slider. Radix and react-aria are accessible by default; both are excellent.
Should I support vertical orientation?
Rarely needed. If asked, swap the math (Y instead of X) and aria-orientation. Most products do not need vertical sliders.
What about non-linear scales?
Logarithmic scales (e.g., for monetary ranges) need value-to-position and position-to-value functions. Provide them as props; the rest of the component is unchanged.