Re: [csswg-drafts] [css-pseudo-4] Standardizing input[type="range"] styling (#4410)

# Problem 3: the `thumb` motion restriction

This is one of the most common and annoying issues I encounter when styling range inputs.

## Background/ what causes it

In WebKit browsers, where the `thumb` is a child of the `track`, the `border-box` of the `thumb` moves within the limits of the `content-box` of the `track`. When the slider is at its minimum value, the left edge of the `border-box` of the `thumb` coincides to the left edge of the `content-box` of the `track`. When the slider is at its maximum value, the right edge of the `border-box` of the `thumb` coincides to the right edge of the `content-box` of the `track`.

This can be seen below, where the `input[type='range]` (gold), the `track` (tomato red) and the `thumb` (purple) all have non-zero `border` and `padding`. For all three, the `padding` area is transparent, while the `border` area and the `content-box` are semi-transparent.

![Animated gif. Shows what happens in WebKit browsers when moving the thumb from the minimum to the maximum value. The thumb only goes within the left and right limits of the track's content-box.](https://user-images.githubusercontent.com/76854602/160995689-ad247a20-0cff-40f6-99c9-348f332d5b75.gif)

In Firefox, where the `thumb` is a sibling of the `track`, the `border-box` of the `thumb` moves within the limits of the `content-box` of the `input[type='range]`. When the slider is at its minimum value, the left edge of the `border-box` of the `thumb` coincides to the left edge of the `content-box` of the `input[type='range]`. When the slider is at its maximum value, the right edge of the `border-box` of the `thumb` coincides to the right edge of the `content-box` of the `input[type='range]`.

![Animated gif. Shows what happens in Firefox when moving the thumb from the minimum to the maximum value. The thumb only goes within the left and right limits of the slider's content-box.](https://user-images.githubusercontent.com/76854602/160997510-96dc6272-5c37-40de-9b8d-a999583da102.gif)

*Note that making the `border-box` of the `track` shorter or longer than the `content-box` of the `input[type='range']` by setting a `width` value that doesn't match on the `track` doesn't work in WebKit browsers - this `width` set on the track simply gets ignored. We can however get the same effect using a lateral `margin` ([tests](https://codepen.io/thebabydino/pen/qVJzNw/) for this).*

This means we need to have no `margin`, no `border` and no `padding` on the `track` - otherwise, WebKit browsers and Firefox will produce different results for the exact same styles.

Here's an illustration of the problem when the `track` has non-zero `margin`, `border` and `padding`. In WebKit browsers, the `thumb` cannot go beyond the `content-box` of the `track`:

![Animated gif. Same styles, WebKit result - the thumb moves such that, at the minimum value, the left edge of the thumb's border-box coincides to that of the track's content-box and, at the maximum value, the right edge of the thumb's border-box coincides to that of the track's content-box.](https://user-images.githubusercontent.com/76854602/160999122-5b66ea54-7f53-4726-9be2-7682ea93cbcc.gif)

In Firefox, it can:

![Animated gif. Same styles, Firefox result - the thumb moves such that, at the minimum value, the left edge of the thumb's border-box coincides to that of the input's content-box and, at the maximum value, the right edge of the thumb's border-box coincides to that of the input's content-box.](https://user-images.githubusercontent.com/76854602/160999418-24502dfa-1bec-451a-8d09-a62684602535.gif)

Now consider the same example I've used before, where the `thumb` diameter is much bigger than the `track` height. The WebKit case, which doesn't allow the `thumb` to go outside the `content-box` of the `track` is terribly inconvenient in this situation (and in most situations, this is a very common problem when coding range inputs). The annotated screenshot below shows how at the minimum slider value the left edge of the `thumb`'s `border-box` doesn't coincide to that of the `track`'s `content-box`.

![Screenshot. Illustrates the difference between the left edge of the thumb's border-box and the left edge of the track's content-box at the minimum slider value given that the track nodes and the thumb need to be middle aligned.](https://user-images.githubusercontent.com/76854602/161005838-15d5af55-4883-4cb2-a563-a15e414a3fa9.png)

Since the `thumb` needs to be middle aligned both horizontally and vertically with the much smaller `track` nodes and the `thumb` diameter (that is, the size of its `border-box`) is bigger than the height of the `track` 's `content-box` (which gives us the diameter of the inner `track`/ progress nodes), it results that the `border-box` of the `thumb` cannot move within the limits of the `track`'s `content-box`, it needs to go outside.

But WebKit browsers do not allow this.

## Workarounds I've used

In this particular case, I went around the problem by emulating the `track` with a `::before` pseudo on a wrapper around the `input[type='range']`. I didn't have a better option for getting the `track` shape anyway.

In other cases, when the `thumb` diameter is only slightly bigger than the `track` height, I simply gave the `thumb` the same diameter as the `track` height and then scaled the `thumb` up a little bit (using a `transform`).

Neither of these two workarounds are ideal when we don't have another reason to add an extra elements and the `thumb` diameter needs to be much bigger than the `track` height.

So what I've done most often was not give any visible styles (no `background`, no `box-shadow` or anything like that) to the pseudo/ element to whose `content-box` the `thumb` motion is restricted and set the `track` styles elsewhere.

In Firefox, where the `thumb` moves within the limits of the `content-box` of the actual `input[type='range']` (not styled in any visible way), we set the `track` styles on the (shorter than the range input) `::-moz-range-track`.

In WebKit browsers, where the `thumb` is a child of the `track` and its motion is restricted within the limits of its parent's `content-box`, we cannot set the visible track styles on `::-webkit-slider-runnable-track`. We can however make the `::-webkit-slider-runnable-track` extend beyond the lateral ends of the actual range input using a negative lateral `margin` and then set the visible `track` styles on the range input itself.

We can use having different pseudos in different browsers (`::-webkit-slider-runnable-track` and `::-moz-range-track`) in order to set one set of styles for the `track` in WebKit browsers and another set of styles in Firefox. But we cannot do the same for the actual `input[type='range']` unless we rely on something like this:

```
/* Firefox input[type='range'] styles */

@supports selector(::-webkit-slider-runnable-track) {
  /* WebKit input[type='range'] styles */
}
```

... which doesn't appear to work in Safari (tested on Linux via Epiphany), according to [this test](https://codepen.io/thebabydino/pen/WNdEvzq), in spite of [MDN saying otherwise](https://developer.mozilla.org/en-US/docs/Web/CSS/@supports#browser_compatibility). Even if it did work in Safari, [mobile support is a problem](https://caniuse.com/mdn-css_at-rules_supports_selector).

We could switch it up and have:

```
/* WebKit input[type='range'] styles */

@supports selector(::-moz-range-track) {
  /* Firefox input[type='range'] styles */
}
```

This works now, though it didn't work when I needed it in the past, which was why back then I resorted to a different solution in WebKit browsers: using this pesky little element inside `input[type='range']`:

![Screenshot highlighting the ::-webkit-slider-container](https://user-images.githubusercontent.com/76854602/160776086-7c90cc8d-5b69-482a-b27b-b5c1f3a11239.png)

It's `read-only`, so I undid that, gave it the `track` styles making it a bit shorter than the actual range input and then used a negative margin on `::-webkit-slider-runnable-track` (which was left with no styles that would make it visible) to bring it back to the size of the `input[type='range']`.

I know it's hacky, but, at the time, it was the only way I could come up with in order to get such sliders to work the same way in all browsers without adding an extra element to visually reproduce the `track` or without coding a tiny `thumb` which would then get scaled up using a `transform`.

## How this should work

Well, there are multiple options.

**One**, the simpler option. The `thumb`'s vertical midline should move from a distance `d` away from the left edge of the `track`'s `content-box` to the same distance `d` away from the right edge of the `track`'s `content-box`, where `d` is half the `height` of the `track`'s `content-box`.

```
d = .5·H₀

H₀ = height of track content-box
```

This would ensure consistent alignment for sliders with the same track and ruler size, but differently sized thumbs:

![Screenshot. Range inputs with gradient progress and differently sized thumbs.](https://user-images.githubusercontent.com/76854602/161096630-06fd6465-e2ff-4cd4-a349-f46ca92bd93a.png)

**Two**, probably the more correct option. Because if there's something that bothers me about the above, is that it feels that the second longer thumb goes too far outside the track.

The `thumb`'s vertical midline should move from a distance `d` away from the left edge of the `track`'s `content-box` to the same distance `d` away from the right edge of the `track`'s `content-box`, where `d` is the maximum between half the `height` of the `track`'s `content-box` and half the `width` of the `thumb`'s `border-box` minus half the difference between the `height` of the `thumb`'s `border-box` and the `height` of the `track`'s `content-box`.

```
d = max(.5·H₀, .5·w₂ - .5·(h₂ - H₀))

H₀ = height of track content-box
w₂ = width of thumb border-box
h₂ = height of thumb border-box
```

For a `thumb` with an `aspect-ratio` of `1/ 1`, the two options are equal.

This would ensure consistent distance between the horizontal and vertical edges of the `thumb` and `track` boxes when the slider is at the minimum/ maximum value (and also, if the `track` and `thumb` are both pill-shaped along the same direction, having corner roundings that are half their smallest dimension, these roundings would be on concentric circles when the slider is at the minimum/ maximum value).

For example, take the case of the pill-shaped `thumb` of the third slider below - this would go too far out of the track using the first option, but looks just right using this second option.

![Screenshot of 4 realistic sliders.](https://user-images.githubusercontent.com/76854602/161691738-a89469b5-b58f-4472-b1e5-597add62ef30.png)

(As before, these are all screenshots of sliders I've coded.)

**Three**, allow setting this manually somehow? I've definitely come across cases where the design required the bigger-than-track-height thumb to not even go all the way to the end of the track - sliders 1, 3 and 4 below for example:

![Screenshot of four realistic sliders.](https://user-images.githubusercontent.com/76854602/161690882-db202637-e48c-467f-b0a9-6e2ed9d12d81.png)

Or this slider:

![Screenshot of realistic slider with ruler and progress.](https://user-images.githubusercontent.com/76854602/161691082-985e06a8-cc2f-4008-ad99-f57ccc357eb8.png)


-- 
GitHub Notification of comment by thebabydino
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/4410#issuecomment-1088315382 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Tuesday, 5 April 2022 06:27:52 UTC