primitives
primitives copied to clipboard
[Slider] Incorrect range mapping
Bug report
Dragging the thumb or clicking on the track.
Current Behavior

Expected behavior

Reproducible example
Suggested solution
Modify the mapping function.
$$ \frac12W_{thumb}+V_{percent}(W_{track}-W_{thumb}) $$
Additional context
Other implementations:
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range
- https://react-spectrum.adobe.com/react-spectrum/Slider.html
Note: There is also another type of slider:

- https://mui.com/material-ui/react-slider/
- https://baseweb.design/components/slider/
Your environment
| Software | Name(s) | Version |
|---|---|---|
| Radix Package(s) | @radix-ui/react-slider | 1.1.0 |
| React | n/a | |
| Browser | ||
| Assistive tech | ||
| Node | n/a | |
| npm/yarn | ||
| Operating System |
Hey @latin-1, the current implementation is by design. We interpolate the size of the thumb over the whole range so it doesn't get out of bounds (outside of its natural bounding box).
What is "correct" is up to interpretation, for example, our implementation matches the native input range on MacOS:
That said, I agree that this is perhaps a little opinionated, and something we have discussed about revisiting at some point in the future, by potentially providing a way to opt into different positioning strategies.
@benoitgrelard You misunderstood what I meant.
The native one and yours do not behave in the same way. For example, if I press the mouse at the center of the thumb and then drag to the right edge, the cursor should remain at the center. If I click close to the edge, it should seek to the track edge (the min/max value) rather than a value close to the edge. That's how the native one act.
The third picture is just for reference. It's not related to this issue.
// current behavior:
value = event.offsetX / tracker.width
// expected behavior:
value = clamp((event.offsetX - thumb.width / 2) / (tracker.width - thumb.width), 0, 1)
That's not the behaviour I see, you can clearly see it jump here:
https://user-images.githubusercontent.com/1539897/220157300-0800a81e-1f4f-4f5b-98ae-1acebde6e921.mp4
@benoitgrelard I mean close to, especially, the distance between the cursor and the edge should less than half of the thumb size.
and in your video, after seek, the cursor is at the center of the thumb. but it's not true for radix-ui
I'm sorry I'm not really following. Your initial diagram are showing one thing, and you're talking about something else now which isn't super clear. The position of the the native one seems to jump too depending on where the cursor is when starting the slide.
and in your video, after seek, the cursor is at the center of the thumb. but it's not true for radix-ui
Yeah I see that, but I don't think one is necessarily better than the other, that's just a slight different in implementation. We interpolate the thumb width over the whole range, ours is entirely driven on the pointer position.
Yeah I see that, but I don't think one is necessarily better than the other, that's just a slight different in implementation. We interpolate the thumb width over the whole range, ours is entirely driven on the pointer position.
I don't think so. No one other than radix-ui implements the slider in this way. You can try to increase the thumb size and see the weird behavior. While dragging, the thumb does not follows the cursor properly.
While dragging, the thumb does not follows the cursor properly.
I know it doesn't, as I said "we interpolate the thumb width over the whole range" and because we keep it in-bound it will look as if it's moving slightly under the pointer.
In our project, we use a large thumb (~100px) and that is quite problematic.
and because we keep it in-bound
It's not related to whatever in-bound or out-bound. No one other than radix-ui implements the in-bound slider in this way.
I haven't seen many examples of libraries even having an in-bound layout like this. Do you know any so we can compare?
I haven't seen many examples of libraries even having an in-bound layout like this.
I don't have either. But as far as I can tell, the iOS one, the Web's native one, the one in Adobe Spectrum, are behave in the same way, which is different from yours.
Here, to make sure we are talking about the same thing. This is what I mean by:
We interpolate the thumb width over the whole range
Current one (in bounds), with the compensation of the thumb width:
https://user-images.githubusercontent.com/1539897/220164099-15437cc2-5dce-4783-b776-c09781b131ca.mp4
If we didn't do compensate to keep in bounds (what most implementations do, thumb goes outside of bounding box):
https://user-images.githubusercontent.com/1539897/220164144-3d5366cb-7a23-473b-9547-393c0624850f.mp4
So you can see that what you are describing is directly related to the compensation to keep in-bounds.
So you can see that what you are describing is directly related to the compensation to keep in-bounds.
I don't think so. For example, React Spectrum, which is in-bound, acts like the second one with a slightly longer track. That's the common pattern for in-bound sliders. The Web's native one does the same.
AFAIK, the only other implementation interpolate the position over whole range, is the WAI-ARIA's color viewer slider. But the multi-thumb slider example from WAI-ARIA just follows the native one. It's hard to say which is recommended from WAI-ARIA.
https://www.w3.org/WAI/ARIA/apg/patterns/slider/examples/slider-color-viewer/ https://www.w3.org/WAI/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb/
It's not in-bounds right? Or am I missing something?
https://user-images.githubusercontent.com/1539897/220168428-d27561de-d8c8-407c-a2a4-5f8d70986954.mp4
@benoitgrelard https://react-spectrum.adobe.com/react-spectrum/Slider.html I mean this one.
Oh I see weird, their react-aria seems to be out-bounds, but the react-spectrum one which is built on top seems to be in-bounds as you point out. I can't figure out what they're doing to change that though looking at the source… 🤔
I think I found it, a combination of margin compensation and width:
Yep, if I deactivate our compensation, we can still achieve in-bounds using negative margins like this:
https://user-images.githubusercontent.com/1539897/220171672-6c44113a-22cf-4d6f-929a-c2396ce5c30f.mp4
Can you confirm this is what you'd be after?
Yes that's what I mean. Currently, the only way to achieve this is, using a zero-width thumb container.
Ok, thanks for confirming. So one thing we could do potentially is offer a way to opt-out of the thumb compensation. Would that be good for you?
Yes and no. I still think the current behavior is problematic though. Anyway, I'm OK with a new option. It's also useful for constructing an out-bounds slider.
What would this not enable?
I'm fine with either as long as the behavior is well documented.
Hello, I'd like to chime in here, if I can.
Isn't current behaviour a bit problematic with a thicker range?
As the range doesn't end in the middle of the thumb, if both have a border radius, we'll see a gap between them for value close to the minimum.
@latin-1's suggested solution would solve this problem.

I worked around it by turning the thumb into 0 width container with the :after, but that gets me an out of bounds slider, and I'd rather have an in-bounds one but without the gap.
Rephrasing for better understanding: I like the current Thumb position, but not the current Range width.
Can you post a sandbox of your case @hasparus?