base-ui icon indicating copy to clipboard operation
base-ui copied to clipboard

[slider] `Control` sizing, inset slider design

Open vladmoroz opened this issue 1 year ago • 7 comments

  • 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)
  • minStepsBetweenValues name 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
    • ✅ ~Note: when closing this issue, please make sure that this and this kludge in the demos are removed.~ https://github.com/mui/base-ui/pull/1054
  • ✅ ~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.~ https://github.com/mui/base-ui/pull/1017

Search keywords:

vladmoroz avatar Dec 06 '24 14:12 vladmoroz

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

mj12albert avatar Dec 06 '24 15:12 mj12albert

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?

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

image

vladmoroz avatar Dec 06 '24 15:12 vladmoroz

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

vladmoroz avatar Dec 06 '24 16:12 vladmoroz

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 avatar Dec 08 '24 03:12 colmtuite

@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:

image

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.

vladmoroz avatar Dec 08 '24 09:12 vladmoroz

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

colmtuite avatar Dec 09 '24 15:12 colmtuite

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

mj12albert avatar Dec 09 '24 16:12 mj12albert

Control is 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)?

mj12albert avatar Mar 28 '25 09:03 mj12albert

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.

vladmoroz avatar Aug 15 '25 14:08 vladmoroz

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

mj12albert avatar Aug 16 '25 00:08 mj12albert

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

mj12albert avatar Aug 18 '25 03:08 mj12albert

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

github-actions[bot] avatar Sep 30 '25 11:09 github-actions[bot]