Skip to content

[Slider] Add 'thumbAlignment' option to align thumb center with slider edges#2806

Open
mark-gl wants to merge 6 commits intoradix-ui:mainfrom
mark-gl:thumb-alignment
Open

[Slider] Add 'thumbAlignment' option to align thumb center with slider edges#2806
mark-gl wants to merge 6 commits intoradix-ui:mainfrom
mark-gl:thumb-alignment

Conversation

@mark-gl
Copy link
Copy Markdown

@mark-gl mark-gl commented Mar 27, 2024

Description

Added a thumbAlignment prop that provides a choice between containing thumb sliders within the track or allowing them to centre on the edges of the track. This replicates the behaviour of the Ark/Zag.js slider prop with the same name.

By default, Radix's slider thumb position interpolates to ensure it stays within the track. The centred alignment option might be more convenient in some use cases, since the mouse will always be at the centre of the thumb when dragging. MUI and Base Web use this approach as well.

This PR also adjusts the default thumb behaviour to prevent the 'jump' that occurs when dragging the thumb from the slider edges. This is achieved by ignoring cursor movements for half of the current thumb's width (or height for vertical sliders) on either edge of the track.

The two options can be visualised by the images from #1966:

thumbAlignment: "contain" (default)

image

thumbAlignment: "overflow"

image

@ADTC
Copy link
Copy Markdown

ADTC commented Mar 30, 2024

Is it possible for you to add a third option to do this?

thumbAlignment: "compensate"

Expected Behavior

I think the contain option should be fixed to do this, but if the library maintainers are saying the wrong version is "by design" then let us at least have the correct version "by option" 🙂

Maybe call it compensate. (Referring to the option compensating for the thumb width. See my comment in the issue.)

PS: If you think about this, it's really just the same as center with extra visual padding on the track that's half the width of the thumb. So it looks like it's in-bound but behaves like it's out-bound. With the center option, I can at least add some CSS to the track to achieve the desired effect (looking inbound while being outbound). But it doesn't look like it would be possible in contain because the mouse behavior itself is broken and no amount of CSS on the track can fix it.

To clarify, we start first with the center option which contains the real track from min to max:

Another Type

Then we just have to add two fake tracks to the two sides of the real track. The fake ones would each be half the width of the thumb. This would give the compensate version.

Copy link
Copy Markdown

@denzellgh denzellgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!🚀

@kydecker
Copy link
Copy Markdown

kydecker commented Jul 8, 2024

Thanks for this PR! I'm currently running into the same issue — track width and border radius creates a gap at the start of the track with the current implementation. This proposal would fix the problem.

CleanShot 2024-07-08 at 10 25 23@2x

To summarize, there are two main issues with the default implementation today:

  1. At small ranges the width of the Range creates a gap with the Thumb.
  2. Due to the way the thumb position is interpolated across the range of the slider, it creates cursor drift.

I like the proposal here and would love to see it merge! However:

Having 3 different options for a prop where two look nearly-identical and only differ in behind-the-scenes implementation feels like a confusing experience for users.

Instead of having three thumbAlignment options, could the default implementation (which is buggy, not actually a desirable behavior) be replaced with what @ADTC is proposing as "compensate"?

Thus we'd have two options for thumbAlignment:

  1. contain (visually remains almost identical to the current implementation, but behind the scenes uses the compensate positioning logic proposed in [Slider] Incorrect range mapping #1966 to fix the gap and cursor drift)
  2. center overflow (allows the thumb to break out of the slider's bounding box)*

The name center feels ambiguous in that it's unclear which direction (vertical or horizontal) it refers to—overflow feels like a better name to me, and pairs better with contain.

Happy to contribute here to get this over the finish line. Thanks for all the work and discussion so far! 🎉

@mark-gl
Copy link
Copy Markdown
Author

mark-gl commented Jul 24, 2024

The problem I'm having with implementing a 'compensate' mode is that the slider range would either have to be always partially filled or never full in order for it to line up with the thumb consistently (here the thumb is transparent):

Slider range at minimum
slider-empty

Slider range at maximum
slider-full

To try and get around this I've put together a concept for a 'compensate' mode that keeps the original interpolation so that the slider range is still empty at the minimum and full at the maximum, but the thumb now behaves in the way you’ve described (at the slider edge, dragging the first half of the thumb has no effect). Try it out and let me know what you think @ADTC:

https://mmxt9p-9009.csb.app/?path=/story/components-slider--thumb-alignment

This implementation won’t solve the issue @evadecker is having with the border radius, but I think you can work around that with some CSS pseudoelement tricks to fill the area next to the thumb similar to what I’ve done here (see MediaSlider.module.css).

@chaance chaance added Resolution: Needs Investigation This issue needs more investigation Priority: Medium Medium priority issue labels Sep 27, 2024
@mark-gl mark-gl requested a review from chaance as a code owner October 19, 2024 17:56
@mark-gl
Copy link
Copy Markdown
Author

mark-gl commented Oct 19, 2024

Given that I can't think of a scenario where anyone would prefer the 'contain' behaviour over the proposed 'compensate' behaviour I linked above, I've gone ahead with @evadecker's suggestion of replacing the default behaviour and renaming the alternative mode to 'overflow'.

This does increase the scope of the PR, but hopefully it will be a suitable resolution for #1966.

@ADTC
Copy link
Copy Markdown

ADTC commented Oct 19, 2024

I apologize for not responding sooner but I had kinda moved on to other things. But yes, I don't think anyone in the right mind wants a "contain" option where the mouse behaviour is weird. The "compensate" option (as I have originally described it, with two half-thumb-width fake tracks added to "center") is the right way to go and it should actually be the default behavior IMO, or at least an option.

PS: You can call it whatever you want, as long as the name is clear. :)

@jbobrow
Copy link
Copy Markdown

jbobrow commented Aug 26, 2025

Would love to see this one resolved. It is really egregious in our use-case.

ColorMap.mov

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 1, 2026

🦋 Changeset detected

Latest commit: cae8433

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@radix-ui/react-slider Minor
radix-ui Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@mark-gl mark-gl requested a review from denzellgh April 8, 2026 14:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Priority: Medium Medium priority issue Resolution: Needs Investigation This issue needs more investigation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants