Csswg-drafts: [css-overflow-4] drawing over the space reserved by `scrollbar-gutter`

Created on 17 Jun 2020  Â·  7Comments  Â·  Source: w3c/csswg-drafts

The scrollbar-gutter spec.

There are cases when authors would like to reserve space for the scrollbar inside a container by using scrollbar-gutter, and they would also like to have certain elements that span the whole width of that container (and therefore would step over that gutter space).

The best example of this would be headers and dividers inside a list. Something like this:

issue_1

However, it is not possible to implement that design with the current definition of scrollbar-gutter: there would be an empty gap on the side of the headers/dividers because they can not extend over the gutter:

issue_2

This issue came up during the discussion about implementing scrollbar-gutter in Chromium (thread) and I think that it is a valid criticism of the spec. I am not sure about what changes or additions could be done to fix it.

css-overflow-4

Most helpful comment

Part 1: Why I think variable / unit based proposals wouldn't work

Having access to scrollbar / gutter width via env() or something like that is not that straightforward:

  • it would probably needs to be a unit rather than an env() variable, otherwise differently sized gutters per element based on scrollbar-width: thin would not be possible.
  • Depending on bidi and UA decisions, the scrollbar might be on either side of the element. Since this cannot practically be determined by a stylesheet author, this further reinforces the idea that it needs to be a unit rather than an env() variable so that it can be per element, and you'd actually need two of them:: one per side.
  • For the use case described here, you'd want a (left/right pair of) unit(s) that is:

    • 0 for overlay scrollbars paired with the stable value (since that doesn't create a gutter and you must not poke out of the element)

    • equal to the scrollbar with for overlay scrollbars paired with the always value

    • equal to the scrollbar with for classic scrollbars with overflow:auto and scrollbar-gutter:stable or scrollbar-gutter:always if there is no overflow (and thus no visible scrollbar in the gutter).

    • 0 for classic scrollbars with overflow:auto and scrollbar-gutter:auto (because you don't want to overflow under the scrollbar if it's there, nor out of the element if it's not)

    • 0 for classic scrollbars with overflow:scroll

  • If we ever want to offer authors the choice between overlay and classic scrollbars through a property, or if we want to allow the UA to make that decision differently on different elements (maybe form some form controls could be different), the (pair of) units is needed to reflect that choice locally.

Variable / units have also been suggested (in https://github.com/w3c/csswg-drafts/issues/4674#issuecomment-577662037) as potential alternatives to the always, force, and/or both values, but that would be hard for the same reasons, and also because the cases where you want the unit to be 0 or to match the scrollbar width vary depending on whether you want to use it for the use case under discussion here, or as alternative to always, or to force, or to both. For instance, unlike the case discussed above, if you're trying to use Variable / units as a replacement for force, you'd need them to be always non zero in the case of classic scrollbars. But the cases where you're want it to be zero would be something else yet again if you're trying to use it as a replacement for both.

So if we end up needing a pile of units which are zero in various cases, doubled up to deal with left/right, this is starting to look like variables or units is either not going to work, or is going to be highly confusing.

Part 2: Alternative proposal.

However, based on an (offline) discussion with @felipeerias, we have found a potential solution.

scrollbar-gutter could have one more value: match-parent.

  • On a scroll container, this has no effect
  • on a (non scroll container) block level element, if it's parent has no scollbar-gutter, it has no effect
  • on a (non scroll container) block level element, if its parent has a scrollbar gutter, then the element also has a gutter:

    • on the same side(s) as the gutter(s) its parent (plural is in the case the parent has both)

    • of the same width as that of its parent (taking into account, if necessary, scrollbar-width on the parent)

    • that is made to overlap (i.e. collapse) with that of its parent

So, here's an example, where you have a scroller, with a gutter, and one of its children wants to extend its background into the parent's gutter, you set scrollbar-gutter: match-parent on that child, and you get this:

Screenshot 2020-06-23 18 42 22

And this "just works", even if the parent and child have a different bidi direction, even if the parent has a gutter on both sides, regardless of whether the scrollbar is overlay or classic, etc…

More details:

  • if the match-parent child has a non-zero border and / or margin on the side where the gutter is expected, then the size of the child's gutter is parent.gutter - child.border - child.margin, and the child's gutter+border+margin is what collapses with the parent's gutter.
  • It could be based on the nearest ancestor with a gutter, rather than the parent, but:

    • dealing with padding/border/margins of inbetween elements which don't have match-parent seems troublesome.

    • going for the parent avoids expensive lookups far up the tree, unless they're actually needed (in which case you set match-parent on the in-between nodes).

    • to avoid unintentional collapsing with the gutters of the root element.

  • this works best if scrollbar-gutter is not inherited, as discussed in https://github.com/w3c/csswg-drafts/issues/5231

All 7 comments

width: 100% ignore-gutter?

width: 100% ignore-gutter?

I still hope we can agree on an env() way of accessing the computed scrollbar size for solving these cases. We could then leverage things like calc(100% - env(scrollbar-width)) or calc(100% + env(scrollbar-width)) to move things in or out of the gutter, whether scrollbars take up space or just "overlay" content.

See also https://github.com/w3c/csswg-drafts/issues/2630

@jonjohnjohnson - that sounds helpful (though I think there is room for both of them, one is a shortcut for most cases and the other is a good primitive to have when you need specific calculations).

Part 1: Why I think variable / unit based proposals wouldn't work

Having access to scrollbar / gutter width via env() or something like that is not that straightforward:

  • it would probably needs to be a unit rather than an env() variable, otherwise differently sized gutters per element based on scrollbar-width: thin would not be possible.
  • Depending on bidi and UA decisions, the scrollbar might be on either side of the element. Since this cannot practically be determined by a stylesheet author, this further reinforces the idea that it needs to be a unit rather than an env() variable so that it can be per element, and you'd actually need two of them:: one per side.
  • For the use case described here, you'd want a (left/right pair of) unit(s) that is:

    • 0 for overlay scrollbars paired with the stable value (since that doesn't create a gutter and you must not poke out of the element)

    • equal to the scrollbar with for overlay scrollbars paired with the always value

    • equal to the scrollbar with for classic scrollbars with overflow:auto and scrollbar-gutter:stable or scrollbar-gutter:always if there is no overflow (and thus no visible scrollbar in the gutter).

    • 0 for classic scrollbars with overflow:auto and scrollbar-gutter:auto (because you don't want to overflow under the scrollbar if it's there, nor out of the element if it's not)

    • 0 for classic scrollbars with overflow:scroll

  • If we ever want to offer authors the choice between overlay and classic scrollbars through a property, or if we want to allow the UA to make that decision differently on different elements (maybe form some form controls could be different), the (pair of) units is needed to reflect that choice locally.

Variable / units have also been suggested (in https://github.com/w3c/csswg-drafts/issues/4674#issuecomment-577662037) as potential alternatives to the always, force, and/or both values, but that would be hard for the same reasons, and also because the cases where you want the unit to be 0 or to match the scrollbar width vary depending on whether you want to use it for the use case under discussion here, or as alternative to always, or to force, or to both. For instance, unlike the case discussed above, if you're trying to use Variable / units as a replacement for force, you'd need them to be always non zero in the case of classic scrollbars. But the cases where you're want it to be zero would be something else yet again if you're trying to use it as a replacement for both.

So if we end up needing a pile of units which are zero in various cases, doubled up to deal with left/right, this is starting to look like variables or units is either not going to work, or is going to be highly confusing.

Part 2: Alternative proposal.

However, based on an (offline) discussion with @felipeerias, we have found a potential solution.

scrollbar-gutter could have one more value: match-parent.

  • On a scroll container, this has no effect
  • on a (non scroll container) block level element, if it's parent has no scollbar-gutter, it has no effect
  • on a (non scroll container) block level element, if its parent has a scrollbar gutter, then the element also has a gutter:

    • on the same side(s) as the gutter(s) its parent (plural is in the case the parent has both)

    • of the same width as that of its parent (taking into account, if necessary, scrollbar-width on the parent)

    • that is made to overlap (i.e. collapse) with that of its parent

So, here's an example, where you have a scroller, with a gutter, and one of its children wants to extend its background into the parent's gutter, you set scrollbar-gutter: match-parent on that child, and you get this:

Screenshot 2020-06-23 18 42 22

And this "just works", even if the parent and child have a different bidi direction, even if the parent has a gutter on both sides, regardless of whether the scrollbar is overlay or classic, etc…

More details:

  • if the match-parent child has a non-zero border and / or margin on the side where the gutter is expected, then the size of the child's gutter is parent.gutter - child.border - child.margin, and the child's gutter+border+margin is what collapses with the parent's gutter.
  • It could be based on the nearest ancestor with a gutter, rather than the parent, but:

    • dealing with padding/border/margins of inbetween elements which don't have match-parent seems troublesome.

    • going for the parent avoids expensive lookups far up the tree, unless they're actually needed (in which case you set match-parent on the in-between nodes).

    • to avoid unintentional collapsing with the gutters of the root element.

  • this works best if scrollbar-gutter is not inherited, as discussed in https://github.com/w3c/csswg-drafts/issues/5231

What if we need it the width of the element in red to be the way it is in the example, but that element also has a scroll bar (say, a left-to-right one, so the two do not collide)? Do we need scrollbar-gutter-outside and scrollbar-gutter-inside?
Also, I have not checked, but does scrollbar-gutter let you set different values for horizontal and vertical scrollbars?
For right-to-left and left-to-right (or does :dir take care of that?)?

I have been exploring @phistuck's scenario and match-parent does some funky things when applied on a scrolling element:

  • when both scrollbars are adjacent, the inner element's gutter will have to accommodate _both_ the parent's gutter and the element's own scrollbar (check out the red element in the figure):

scrollbar-gutter example with match-parent, same edge

  • when the scrollbars are on different edges, the element will have two gutters, one to match its parent's and another to hold its own scrollbar (somewhat similar to both)

scrollbar-gutter example with match-parent, opposite edges

I am tempted to say that this would be working as intended? What do you think?

Finally, in light of the above, I think it would be fine if we allowed to combine match-parent with other values. For example scrollbar-gutter: stable match-parent;:

scrollbar-gutter example with stable match-parent

// @frivoal

@phistuck:

does scrollbar-gutter let you set different values for horizontal and vertical scrollbars?

At the moment, scrollbar-gutter only affects the gutters placed at the inline edges of the box.

If you are using scrollbar-gutter to prevent changes in your layout caused by the scrollbars at the inline edge, you probably also don't want the scrollbars at the block edge to cause similar changes.

One way to ensure that would be to have a reasonable default, e.g. _"if scrollbar-gutter is something other than auto, the gutter in the block edge has the stablebehaviour"_.

Another (complementary?) way would be to offer more fine-grained control over the behaviour of gutters at the block edges.

// @frivoal

Was this page helpful?
0 / 5 - 0 ratings