Range SliderLoading saved progress…

Range Slider

Build a dual-handle range slider as a single React component. The user drags two circular handles along a track to pick a low and a high value between 0 and 100; the segment between them is highlighted and a label reads the current range. The interesting part isn't the markup — it's turning a raw pointer position into a clean integer value, and stopping the two handles from crossing each other.

Task

The starter App.tsx renders the track, the highlighted range, both handles, and the 20 – 80 label in a resting state with no drag logic. Make it interactive:

  1. Hold the state. Keep low and high in useState (start 20 / 80) and grab the track element with a useRef.
  2. Start a drag. On a handle's onPointerDown, add pointermove and pointerup listeners on window (not the handle) so the drag keeps working even when the pointer leaves the handle.
  3. Pointer to value. In pointermove, read the track's getBoundingClientRect() and compute round((e.clientX - rect.left) / rect.width * 100), clamped to 0..100.
  4. Don't let them cross. Set low to min(value, high) and high to max(value, low).
  5. Clean up. On pointerup, remove both window listeners.

Examples

  • The page loads with handles at 20 and 80, a green bar between them, and 20 – 80 below. Dragging the left handle right to the middle updates the label to 50 – 80 and shrinks the green bar.
  • Dragging the left handle past the right one clamps it: it stops at 80, never overtaking the high handle.
  • Releasing the pointer anywhere (even off the handle) ends the drag, because the listeners live on window.

Notes

  • Listen on window, not the handle. A fast drag outsprints the handle; window-level pointermove/pointerup keep tracking until release.
  • Snap to integers. Math.round on the percentage gives whole-number values and a tabular label that doesn't jitter.
  • Clamp against the other handle. low can't exceed high and high can't fall below low — that's the whole "range" guarantee.
  • Styling (track, range fill, handles) is already in styles.css; focus on the state and the pointer math.
Loading editor…
Loading preview…