w3c / csswg-drafts

CSS Working Group Editor Drafts
https://drafts.csswg.org/
Other
4.44k stars 657 forks source link

[css-overflow-4] drawing over the space reserved by `scrollbar-gutter` #5232

Open felipeerias opened 4 years ago

felipeerias commented 4 years ago

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.

phistuck commented 4 years ago

width: 100% ignore-gutter?

jonjohnjohnson commented 4 years ago

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

phistuck commented 4 years ago

@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).

frivoal commented 4 years ago

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:

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.

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:

phistuck commented 4 years ago

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

felipeerias commented 4 years ago

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

scrollbar-gutter example with match-parent, same edge 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

felipeerias commented 4 years ago

@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

Rayraz commented 3 years ago

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.

Maybe my brain is just fried after a long day, but I've read this about 5 times and can't wrap my head around it. Is there any chance you could visualize this?

felipeerias commented 3 years ago

The main problem with scrollbar-gutter is that it doesn't provide a good solution when you want some elements to extend all the way to the edge while others respect the scrollbar gutter.

For example, let's say that you have a header/toolbar and a list of items with text on the left and a checkbox on the right.

When the scrollbar is fixed, you want this:

examplefixedaligned

And when it is overlay, you want this:

exampleoverlaydoesnotcover

Keeping the header aligned is easy with the force keyword. The problem comes when you try to align the scrolling content.

With scrollbar-gutter: stable the overlay scrollbar covers the interactive elements, making the UI hard to use:

exampleoverlaycovers

On the other hand, with scrollbar-gutter: always the background for each item doesn't extent all the way to the edge when the scrollbar is overlay:

exampleoverlaybackgroundissue

You can work around that by using force on each individual item (that's how I created the second image above), but then the layout with fixed scrollbars becomes messed up:

examplefixednotaligned

One solution for the problem described above would be to set scrollbar-gutter: match-parent on each list item so their background extends all the way to the edge but the content inside each of them still respects their parent's scrollbar gutter.

(Images taken from the examples in the explainer)

// @frivoal @Rayraz

felipeerias commented 3 years ago

Whenever scrollbar-gutter comes up, it is often pointed out that an alternative would be to define a set of units or environment variables holding the scrollbar thickness. As explained in https://github.com/w3c/csswg-drafts/issues/5232#issuecomment-648047604 this could end up being quite complex to define and/or to use.

I don't know if this has been proposed before but I wonder if there could be a way to use CSS selectors and media queries to specify styles that should only apply when the scrollbars are overlay, when the scrollbar is on a particular side of the box, etc. Perhaps this could be a way to reduce the number of different variables that would be needed.

For example:

.box {
    scrollbar-width: thin;
}

@media (scrollbar-type: overlay) {
    .box:inline-scrollbar-position(end) {
        padding-inline-end: env(scrollbar-thickness-thin);
    }
}

// @frivoal @Rayraz

Rayraz commented 3 years ago

Thanks, I understand the problem a lot better now. Did you see my suggestion here: https://github.com/w3ctag/design-reviews/issues/563#issuecomment-786023007?

I suggested to treat the scroll-gutter as another layer in the box-model that applies independently from the scrollbar-width. I just noticed I never addressed there how a gutter-box would be different from a padding-box. I think the defining property of a gutter-box should be that scroll chrome always gets rendered as an overlay on top of the gutter. I think that would allow for some pretty decent solutions.

Let's keep the todo-list example, with the following markup :

<div class="view">
    <div class="header">Check all <input type=checkbox></div>
    <ul class="list">
        <li class="item">Lorem ipsum dolor sit amet <input type="checkbox" /></li>
        <li class="item">Consectetur adipiscing elit <input type="checkbox" /></li>

        <!-- ... -->

        <li class="item">Id est laborum <input type="checkbox" /></li>
    </ul>
</div>

If we assume that scrollbar-width: auto; is the default, styles for the todo-list could be something like:

.header, .list {
    scroll-gutter-right: auto; /* gutter-width matches scroll-width: auto; */
}
.item {
    scroll-gutter-right: match-parent;
}

Thin scrollbars with matching thin gutters could be achieved like this:

.header {
    scrollbar-gutter-right: thin;
}
.list {
    scrollbar-width: thin;
    scroll-gutter-right: thin;
}
.item {
    scroll-gutter-right: match-parent;
}

Perhaps some interesting things like this might be in the cards too:

.view {
    scrollbar-width: 8px; /* .list inherits this width */
}
.header, .list {
    scroll-gutter-right: scrollbar-width; /* Match the scrollbar-width from nearest ancestor */
}
.item {
    scroll-gutter-right: match-parent;
}

I realize allowing people to set both scroll-gutter and scrollbar-width explicitly allows for styles that might seem a bit nonsensical at first glance. For example, there is nothing stopping someone from giving an element a scroll-gutter that's narrower than the scrollbar-width, but I don't really think that's a problem. Someone will probably actually find a creative use for it.

chrishtr commented 3 years ago

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:

In this example, the match-parent element overlaps the gutter, and the other elements don't. Isn't that backwards from what you said in your description?

cw789 commented 2 years ago

I'll quickly add my example to this topic as I just stumbled over it. I'm trying to use scrollbar-gutter: stable both-edges; on html.

But I have a loading bar on the top of my page (position: fixed; left: 0; right: 0;). With the current implementation my loading bar is to short on both sides (if no scrollbar) on one side (if there is a scrollbar).

TheElegantCoding commented 6 months ago

Hello same here when you have an element with position: fixed the property scrollbar-gutter: stable doesn't work