[slider] `Control` sizing, inset slider design
- We need a prop or a CSS variable to make a slider to work like Radix/macOS slider—Radix and macOS sliders position the Thumb so that its edges are contained within the track (and we always align the Thumb's center to the track edge instead, so it ends up poking out of the Track at the edges)
-
minStepsBetweenValuesname wasn't too intuitive—will think if there's something better - Cursor movement is tracked incorrectly when the Control part has padding
https://github.com/user-attachments/assets/16a1bf2e-2ead-4555-8b65-167653920706
Not sure what Control sizing has to do anything with the cursor movement; I'd expect it all to be done relative to the Track
Done
- ✅ ~I expected divs instead of spans, would have saved me a couple situations when my width/height styles wouldn't work right away~ https://github.com/mui/base-ui/pull/1017
- ✅ ~Indicator has position absolute, which makes it harder to style. I don't understand how this is helping me. See this PR for an example.~ https://github.com/mui/base-ui/pull/1017
- ✅ ~Thumb has position absolute, which makes sense. However, if it's positioned absolutely, we need to specify relative to what, otherwise it's a gotcha for the dev to figure out and find the Thumb in a random position on the page first. We need to set
position: relativeon the Track.~ https://github.com/mui/base-ui/pull/1017
Search keywords:
I'd expect it all to be done relative to the Track
Movements are actually relative to the Control which is the "tracked area", and the Track is just a dumb component purely for styling the track, because without it you'd have to use a pseudo-element on the Control to style a "visible track" (or insert your own span)... naming issue?
Cursor movement is tracked incorrectly when the Control part has padding
This is probably still a bug, I'll check it out
Movements are actually relative to the
Controlwhich is the "tracked area", and theTrackis just a dumb component purely for styling the track, because without it you'd have to use a pseudo-element on theControlto style a "visible track" (or insert your own span)... naming issue?
I was explained that Control is the clickable area, so I had used a bit of padding on it (including horizontal) to make clicks and taps easier.
I think no matter what size the Control is, the Thumb would always be placed relative to the visible Track.
In the Anatomy we are also placing Thumb into the Track so altogether that's why I expected it to work this way
I'll remove the horizontal padding from Control in my demo just to be safe for now, but let's still take a note to discuss later whether it's a legit design decision
https://github.com/mui/base-ui/pull/980/commits/de8e9ab72c10a437856c270b2a2463aa90b4583d
We need a prop or a CSS variable to make a slider to work like Radix/macOS slider
@vladmoroz "Need" seems a bit strong here? We designed it this way intentionally. When the thumb is contained, the thumb position usually doesn't visually communicate the value accurately, and the pointer becomes misaligned. The current approach seems like a better design?
@colmtuite I'd actually argue that inset thumb has to be the default
the thumb position usually doesn't visually communicate the value accurately
Slider isn't a component for entering precise values anyway, so this doesn't seem like the most important constraint here. Sometimes it's important, sure—I'm well aware of https://github.com/radix-ui/primitives/issues/1966, but precise mapping is more of an edge case for slider in my opinion. It's fine as long as it's going to be an option.
Otherwise, we can't ignore a class of slider designs, like this one:
Centered thumb wouldn't work in UIs like this one too because it would yield a layout collision:
iOS sliders are the same. Because of potential layout collisions and misalignments, I'd almost never want to use a centered thumb—I wouldn't use a slider when a precise value matters anyway.
the pointer becomes misaligned
This has always seemed just like an implementation mistake in Radix. Don't see why it should be misaligned—if thumb has, say, 100px of travel, that should be covered by 100px of pointer travel.
Native range input sliders (which are inset by default) and macOS sliders don't have this issue:
https://jsfiddle.net/1j9x06yu/
One more point to consider is that with Radix, it's possible to implement a centered thumb yourself via a 0px thumb element and pseudo elements for the visual thumb, but the reverse doesn't work—you can't implement an inset thumb with the design that we have.
Altogether, I think we need something like position: "inset" | "centered" on the Thumb, with inset being the default because it slots into any design layout well and can work with two most common slider design styles—a thin track with a larger thumb, and a thick track and same size thumb.
Thumb has position absolute, which makes sense. However, if it's positioned absolutely, we need to specify relative to what, otherwise it's a gotcha for the dev to figure out and find the Thumb in a random position on the page first. We need to set position: relative on the Track.
Yes, if we're setting position: absolute as an inline style, then we must set position: relative as an inline style too. Either we set both inline styles, or leave both up to the user.
Indicator has position absolute, which makes it harder to style. I don't understand how this is helping me.
Hmm right yeah, it's nested inside the track so it just needs a way to handle width
Either we set both inline styles, or leave both up to the user.
How about for all the slider parts we leave the position property up to the user? position is relatively easy to figure out
Controlis the clickable area, so I had used a bit of padding on it (including horizontal) to make clicks and taps easier.
As a workaround, invisible pseudo-elements can be used pad the "poles" of the control to make the hitbox bigger, here's a sandbox (where they have a red background): https://codesandbox.io/p/sandbox/misty-hooks-2f9qgd?file=%2Fsrc%2Findex.module.css
Taps/clicks would immediately set the value to the min or max, is that how it should be expected to work if padding is applied (on all sides)?
Giving inset sliders a shot again as this has been my last component to migrate away from Radix sliders for months, I still don't feel like this is solved with pseudo elements or extra paddings.
Even a basic design adds some non-trivial CSS to get thumbs within the track, and then there are still limitations. If I want a single shadow or gradient to run from start to end of the indicator, that doesn't really seem possible with CSS as now I need to deal with two separate elements.
I also still feel confident that thumbs that poke out are a much worse and much less common design overall.
Here's my app with Radix sliders:
Here's migrated to Base UI slider:
I don't think my design is uncommon, nor is spacing unreasonably tight, but thumbs that poke out just can't work here.
I can find workarounds for my current situation, but that's still annoying.
I think we need something like position: "inset" | "centered" on the Thumb
@vladmoroz The best way is probably with the position: "inset" | "centered" prop as you suggested
Could it be on the root or control instead, just to avoid different thumbs on the same slider with a different position
A trade-off is that the CSS for inset positioning depends on the size of the thumb, we wouldn't be able to SSR the thumbs in that case
This fix will be available in the next npm release of Base UI.
In the meantime, you can try it out on our Canary release channel:
npm i https://pkg.pr.new/@base-ui-components/react@2540