w3c / csswg-drafts

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

[css-values] Should viewport units still depend on scrollbar width for overflow:scroll? #1766

Closed dbaron closed 7 years ago

dbaron commented 7 years ago

Right now the definition of viewport units says that they shrink for scrollbar sizes when overflow on the root is scroll (which is quite rare, since it's not the default), but not in other cases.

Apparently Gecko is the only engine that implements this, and we're wondering whether it's worth the work to maintain that implementation when it seems like nobody else is going to.

Are other engines planning to do this?

dbaron commented 7 years ago

(The Gecko bug in question is bug 1393603.)

tabatkins commented 7 years ago

It's literally the only way to get accurate viewport units in the presence of a root scroller. Use of vw is relatively rare in the first place, and most of the time people don't need precise values, but I suspect there's a subset of authors who are annoyed at 100vw not being the width of the layout area in some browsers.

tabatkins commented 7 years ago

I just asked our relevant implementor (@bokand) for our impl plans.

Here is an example showing off the functionality - in Firefox all 10 floats correctly sit on the same line, but in Chrome the 10th one wraps to the next line.

emilio commented 7 years ago

The problem is that this introduce styling state that depends on other styling state. In particular, changing the overflow on the scroll root would mean recomputing every viewport unit in the document (even the ones higher up in the tree!).

Gecko relied on reconstructing the whole layout tree when this happenned, which happened to restyle the whole document.

As can be seen in http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5339, this is now broken since the optimizations in https://bugzilla.mozilla.org/show_bug.cgi?id=1344398 landed, and on the new style system, which decouples styling from layout tree building, it's similarly broken.

tabatkins commented 7 years ago

(even the ones higher up in the tree!).

This only applies to the root scroller, so at max this means you might be able to change it on body and have it affect html (is that right? I forget what all the complexity around root scrollers means). Blame ancient browsers for screwing up "which element styles the canvas/window?" so bad. ^_^

Aside from that one bit of body->html jumping, this is otherwise identical to recomputing em units based on a font-size change. Which, I know, requires some complexity to handle in a performant manner, but is a proven mechanism at least.


And again, coming at it from the author side rather than impl, not doing this means that it is impossible to write something that wants to be a % of the viewport when scrollbars appear. That sucks, it's the sort of "are you kidding me" moment that super-frustrates authors. It makes viewport units just a broken feature (since scrollbars are, after all, the rule on pages, rather than the exception).

litherum commented 7 years ago

This is a perfect use-case for the variables-that-don't-vary-except-they-do-sometimes proposal that Dean proposed at the Paris F2F.

tabatkins commented 7 years ago

By "perfect use-case" you mean "let the viewport units continue to be broken, and assume that authors will be able to write code that listens for viewport-size changes and updates a const-var every time it changes"? Can you even write the code for that? I'm not sure how to do it, at least.

css-meeting-bot commented 7 years ago

The Working Group just discussed Should viewport units still depend on scrollbar width for overflow:scroll?.

The full IRC log of that discussion <dael> Topic: Should viewport units still depend on scrollbar width for overflow:scroll?
<tantek> because: <dael> jensimmons: I'm fine punting to next week.
<dael> github: https://github.com/w3c/csswg-drafts/issues/1766
<dael> astearns: We're waiting on impl feedback?
<dael> TabAtkins: Yes, I'm getting ?? to comment.
<zcorpan_> (Could css-meeting-bot remove the label when it's commenting?)
<astearns> it does when we resolve something
<dael> dbaron: I reised this bc gecko was the only engine that impl when we decided this years ago. It's apperently harder to do on silo. I think it's reasonable on their part.
<dael> myles: If all browers except Moz ones don't support and MOz wants to remove it seems clear
<dael> TabAtkins: Without this feature there's alot of cases where viewport is broken. THere are plent that do, but I suspect people are avoiding these thigns. You cannot get 100vw to be the size of the screen. THere's usually a scrollbar and 100vw is too big.
<dael> myles: The desc says it's only on sroll not auto
<dbaron> s/silo/stylo/
<dael> TabAtkins: We decided viewport should be resolved at computed. For overflow auto we decided to ignore scrollbar, but w wanted to pay attention when it's there. overflow:scroll causes a scrollbar so we set it to take scrollbar into account in that case.
<dael> myles: Overflow:Scroll on root is not common case
<Florian> q+
<dael> TabAtkins: I agree. But when your scrollbar appears your items to 100vw will overflow horz and cause a scrollbar. The way we decided to let tha tbe solve dyou can set overflow scroll on it explicitly so it fits. Without this viewport units are just broken. The only time they add to the viewport is when you have no scrollbar which is uncommon
<dael> fremy: You still need to know size of scrollbar. It's not a static number.
<dael> Rossen: We're talking about uint value. I'm with TabAtkins. If in the case of overflow: scroll when scrollbar takes spec away from viewable viewport current spec mandates the correct behavior from user PoV. viewport should resolve to are-scrollbars.
<dael> Rossen: Changing spec for interop and going aaginst what makes sense isn't best.
<astearns> ack Florian
<dael> Florian: If we don't drop the thing should we extend it? Even if it's auto?
<dael> TabAtkins: Prob should. Assuing whatever element applies to root scroller. If that works and we define how that would help.
<dael> Rossen: I would argue against that. THe elemnt causing the scrollbar could be defined in vh unit which causes scrollbar and introduces a dependency. IN the case of overflow scroll on the viewport we only need to add computed value and resolve overflow for the root. From there on decide what he value will be.
<dael> fantasai: No one is suggesting auto for scrollbar. There's a scrollbar gutter property that adds extra space.
<dael> Rossen: I was reading the minutes from Florian about perhaps extending to auto.
<Florian> https://drafts.csswg.org/css-overflow-4/#scollbar-gutter-property
<fantasai> Florian was asking about scrollbar-gutte rproperty
<dael> TabAtkins: It's only when you ahve a predicatble value to auto
<fantasai> s/should we extend it/should we extend it to handle the scrollbar-gutter property/
<dael> Florian: Even though you get that you get a predictable size with space reserved but not scrollbar so maybe that shouldn't count. I don't know.
<astearns> q+
<dael> Rossen: Let's assume simple l to r. Whatever an element with abspos top 0 right 0 wherever that positions to shoudl be extent of vh
<dael> TabAtkins: No because then by default auto changes value of vw unit and that defets the computed value time purpose
<dael> Rossen: No in the cae of the gutter we're talking about the prop reserving space for gutter
<dael> TabAtkins: If gutter is predictable yet
<dael> Rossen: That's what I'm talking about. So if abspos items are positioned to gutter this should be extent of vh as well?
<dael> Florian: I don't think we explicitly define it may fall out of the wording, but I don't htink that was concious decision.
<dael> Florian: I don't think we decided on that
<dael> astearns: Is that enough on the gutter sidebar?
<Rossen> q?
<dael> Florian: I think we need to come back, but not for this call.
<dael> astearns: I want to go back to the discussion before gutters where...I agree that vw should take the scrollbar into account where it exists. But the one piece of impl feedback on that is it's really annoying to have to layout arbitrary things when you gain a scrollbar. Would it make sense to define auto same as scroll case for vw? It's always taking into case poss scrollbar?
<dael> TabAtkins: If you have overflow: hidden on root vw doesn't stretch all the way across the screen.
<dael> Florian: Just auto.
<dael> TabAtkins: No, still hidden. Just changing auto doesn't fix dbaron problem. He didn't want size of vw change based on somebody updating [missed]
<dbaron> I think there were actually 2 issues.
<dael> TabAtkins: Change at all is the worry. Main responce is this is no different hen m unit. You change the font and everything below has to change. Only more complicated is that sometimes body can set viewport. So you can effect one element up.
<Rossen> dbaron, can you pls summarize?
<dbaron> One was that dynamic changes to 'overflow' are hard, but that the other is that in Gecko, we need to depend on layout to find out what the scrollbar width is.
<dael> TabAtkins: Aside from that one element jump which is very rare and not important for purpose of tree expence this is identical to setting font size.
<dael> TabAtkins: You dont' need layout this is computed value time
<dael> dbaron: Second thing that made it had is in gecko it's not ready.
<dael> dbaron: [missed]
<dael> dbaron: I'm not even sure it worked rilably. I think it worked most of the time because usually we constructed scrollbars first.
<dael> TabAtkins: Is that b/c scrollbar width is controllable in FF?
<dael> dbaron: It depends on too many OS dependant and that code exists in scrollbar layout.
<dael> TabAtkins: Yeah.
<dael> dbaron: It's possible that the number of things to test is 2 or 3, but that's not the way it was written
<Rossen> wouldn't webkit-scrollbar {width} have the same effect?
<dael> astearns: We're at time. i'm hearing the desire to keep spec behavior as-is, but we'll need other impl to make the changes. WE should continue impl feedback in the issue. We're out of time today, we'll talk next week.
<TabAtkins> s/Yeah./So maybe just moving the code up and piping the data down into layout instead./
tabatkins commented 7 years ago

Summarizing the impl complexity:

  1. Adjusting the viewport unit size based on overflow is about as hard as the em unit, which requires some work to make performant.
  2. Additionally, you might have to pipe the value up one level, if body is controlling the window scrollbar.
  3. In Firefox the size of the scrollbar isn't page-controllable, but it is based on some OS information, which is currently queried only in the scrollbar layout code, which has the practical effect that it depends on layout information. This could be fixed by pulling the OS queries upward and piping the information into both scrollbar layout and viewport unit calculations, but that's new code to be written. (Alternately, just duplicating the queries in the viewport unit calculations.)
  4. In WK-based browsers, the scrollbar size is page-controllable, via the ::-webkit-scrollbar-* pseudo-elements. If this is taken into account, the viewport units become inextricably layout-dependent, which defeats the original effort to make them computed-value-time absolute units.
dbaron commented 7 years ago

I don't think your (1) is quite correct, because testing whether your current calculation is correct is more complex than for em units, and the effect is global rather than local. (The effect with em units can be a wide effect when font-size inherits, but everywhere it happens it's a local effect.)

I also tend to think adding viewport units themselves were a mistake -- something that we thought was simple, but really should have been addressed by layout systems rather than units, and now turn out to be not as simple as we thought.

tabatkins commented 7 years ago

I don't think your (1) is quite correct, because testing whether your current calculation is correct is more complex than for em units,

Only in abstract. In practice, you do the calculations already every layout, to figure out how wide your scrollbar should be. (And if you cache results and only update when necessary, well, that's the exact work you'd need to do for viewport units too.) Once you're already doing the "figure out how wide the scrollbar is" math, checking if you need to update is just a numeric comparison to the current value.

and the effect is global rather than local. (The effect with em units can be a wide effect when font-size inherits, but everywhere it happens it's a local effect.)

font-size almost always inherits; the fact that it's local in application is irrelevant when the entire page gets spammed with "local" updates.

I also tend to think adding viewport units themselves were a mistake -- something that we thought was simple, but really should have been addressed by layout systems rather than units, and now turn out to be not as simple as we thought.

That would have been a huge mistake; one of the big uses of viewport units is piping page-size information into unexpected places, like font-size so your text gets bigger/smaller according to the viewport size. That can't be done with layout systems.

tabatkins commented 7 years ago

That would have been a huge mistake; one of the big uses of viewport units is piping page-size information into unexpected places, like font-size so your text gets bigger/smaller according to the viewport size. That can't be done with layout systems.

(That said, those use-cases don't need precise values; they're not bothered if 100vw isn't precisely the viewport width, they just care that it approximately maps to the viewport width. And the layout use-cases are, generally, better solved by just using Flexbox/Grid. So, uh, it might be possible to convince me that making the viewport units lower-fidelity is acceptable.)

emilio commented 7 years ago

As can be seen in http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5339, this is now broken since the optimizations in https://bugzilla.mozilla.org/show_bug.cgi?id=1344398 landed, and on the new style system, which decouples styling from layout tree building, it's similarly broken.

Just noticed I meant to point to http://software.hixie.ch/utilities/js/live-dom-viewer/?saved=5340 (Same test case, just adding body:hover { overflow: hidden }, to see the effect). This is broken since Firefox 55.

css-meeting-bot commented 7 years ago

The Working Group just discussed Should viewport units still depend on scrollbar width for overflow:scroll?, and agreed to the following resolutions:

The full IRC log of that discussion <dael> Topic: Should viewport units still depend on scrollbar width for overflow:scroll?
<dael> Github: https://github.com/w3c/csswg-drafts/issues/1766
<dael> Rossen_: Can anyone take this?
<dael> TabAtkins: I'm here.
<dael> TabAtkins: This may not be decided yet. Core of the issue is, vw and vh we spec that if you do overflow scroll on root scroller we take into account size of scrollbar so 100vw fills the visual viewport
<dael> TabAtkins: FF has broken code for this, Servo isn't planning on doing it. We, Chrome, don't do it either. dbaron is asking if we can remove that so vw and vh are based on ignoring scrollbar. My org obj is that distroyed usefulness of these and we decided aut would default to no scrollbar size.
<dael> TabAtkins: After we thought more for layout you don't need...I don't htink many if any vw cases need 100 to be exactly the right size. You can use flexbox or gird for simimlar or better. For otehr things with iewport limits such as scaling fonts, they're fine with a little off
<dael> TabAtkins: I could be convinced that vw are lower fidelity.
<dael> fantasai: I'd ask aorund a bit more. I can imagine wanting something to fill the viewport when you click. Doing layout of sizing of tables if they're too big to wrap them in their own scrollbar. i can see cases.
<dael> TabAtkins: I'd like to see examples that aren't solved in other ways. I should look at the table case again.
<dael> smfr: The author of the webkit blog came asking about this He wanted his viewport to be full bleed and 100 vw was triggering full scrollbars
<dael> smfr: This is only an issue for always on scrollbars.
<Rossen_> position:fixed; left:0; right:0; top0; bottom:0;
<Rossen_> :)
<dael> TabAtkins: I suspect that's why it didn't make it into webkit and then chrome inherited that code.
<dael> TabAtkins: You seem to be arguing we should keep spec as-is so it an respond to scrollbars?
<dael> smfr: Not nec. Independantly we should htink of ways to solve this.
<dael> fantasai: We should solve it with these units. If we don't how do we fix this? Another unit?
<dael> Rossen_: fwiw we don't support in edge or ie either. I'm in support of what TabAtkins said and his rational. At the same time I sympathize with fantasai were if we're going to have units to enable this, this is the unit.
<Florian> Remaining CSS-UI Bugs on mandatory requirements with less than 2 implementations: https://pastebin.com/vu1CCTMv
<dael> Rossen_: Sounds to me like none of the impl have it and the only one with it wants to drop and it's broken. If we allow scrollbar styling that would have more severe effects for how we resolve scrollbar width. What are we doing.
<dael> Rossen_: Options are vw always resolved to full viewport ignoring scrollbars. For most scenarios you know how big the iewport is and if you care about scrollbars you can do calc and subtract.
<dael> myles: You can do better with dino's proposal from the last f2f.
<dael> Rossen_: Even better. Love it. That's a great option once we have it.
<fantasai> s/subtact/subtract some pixels/
<fantasai> s/f2f/f2f. One of those variables can be the scrollbar width/
<dael> Rossen_: Those are the options I see, what do we do?
<dael> myles: Sounds like there's agreement to drop.
<fantasai> :(
<dael> Rossen_: Agreed. Obj?
<fremy> fine with dropping
<dael> RESOLVED: Drop the requirement to subtract scrollbar size from vh/vw units for overflow scroll
fantasai commented 7 years ago

Edits checked in. https://github.com/w3c/csswg-drafts/commit/c1c2e82ca98a5a88f811e9d165757136d33f9dd4 @dbaron, would you mind verifying?

coreyworrell commented 6 years ago

I have a use case for this currently, and it is very frustrating that vw still does not take the scrollbar into account even when setting html { overflow-y: scroll; }

https://codepen.io/coreyworrell/pen/YLrwRX

jonjohnjohnson commented 6 years ago

@coreyworrell -> https://github.com/w3c/csswg-drafts/issues/1958

Hoping scrollbar width/height will solve this issue for us in [css-scrollbars].

Or, if not given the ability to size scrollbars, maybe petition for the ability to access their size with an env() variable??? [css-env-1] list of predefined variables

Then we'll at least be able to derive what would be the scrolling elements "clientWidth" with calc()...

Rossen_: Options are vw always resolved to full viewport ignoring scrollbars. For most scenarios you know how big the iewport is and if you care about scrollbars you can do calc and subtract.
coreyworrell commented 6 years ago

@jonjohnjohnson

I don't think being able to set the width of the scrollbar is related to the issue I'm talking about. Unless I'm not understanding what you're saying. You should be able to have a design depend on the available width and have default scrollbars.

The env() variable for scrollbar width would be a suitable fix though, even if very annoying.

I'm just not sure why vw would by default return a value that includes the area taken up by a scrollbar, as web developers avoid causing horizontal scroll at all costs in practice. And if there were a reason to use it, that should be the 'opt-in', not the other way around. But even then, as of now there is no opt-in or opt-out, it just is always included, which is frustrating to say the least.

jonjohnjohnson commented 6 years ago

@coreyworrell the discussion above highlights the factors/limitations for the issue and reasoning behind the current decision.

As far as setting a scrollbars width...

:root {
  --valueThatAlsoSetsScrollbarWidth: 20px;
  --vw: calc(100vw - var(--valueThatAlsoSetsScrollbarWidth));
  scrollbar-width: var(--valueThatAlsoSetsScrollbarWidth);
}
.width-100vw {
  width: var(--vw);
}
.width-50vw {
  width: calc(var(--vw) * .5) ;
}

Update In https://github.com/w3c/csswg-drafts/issues/1958#issuecomment-409682589 it was agreed to have scrollbar-width property not accept lengths, so hopefully we can still get the size the browser uses as an env()? https://github.com/w3c/csswg-drafts/issues/2630#issuecomment-387909043

:root {
  --vw-inside-scrollbar: calc(100vw - env(scrollbar-width));
}
coreyworrell commented 6 years ago

It sounds like you're saying that I would need to explicitly set the width of the scrollbars for the site to be able to use vw in a useable way? How would this make sense when operating systems and browsers all have different default scrollbar widths, I would always prefer to let the OS handle that how they see fit rather than force a set width. Let the OS/UI/browser do its thing and just tell me how much space I have for actual layout without causing horizontal scroll.

Oh well though, at this point the benefit of using vw is that it is even supported in IE9, but if there were to be some new method/unit/way of handling this I'm not so sure the older browsers would get those patches, which would require authors to use a different method regardless.

jonjohnjohnson commented 6 years ago

@coreyworrell Just trying to offer workarounds for your issue, when the decision that was already made on this topic doesn't give you what you need.

Also, I've resorted to hijacking the rem unit for older browsers creating a "viewport unit" based off clientWidth, which you could also create with a calc() leaving the rem alone for modern browsers. Nevertheless, spec things will always/only impact upcoming versions of browsers. Good luck.

coreyworrell commented 6 years ago

@jonjohnjohnson Understood. I appreciate the help and insight. Overall I'm just expressing my frustration with the spec (or the fact the browsers actually haven't stuck to the spec, ie: overflow-y: scroll supposedly disabling this behavior).

jonjohnjohnson commented 6 years ago

@coreyworrell In this thread it shows that a decision was made to remove that part of the spec and why.

When the height or width of the initial containing block is changed, they are scaled accordingly. However, any scrollbars are assumed not to exist. https://github.com/w3c/csswg-drafts/commit/c1c2e82ca98a5a88f811e9d165757136d33f9dd4

Making it so browser implementations are actually compliant.

coreyworrell commented 6 years ago

@jonjohnjohnson Doesn't make it okay or the correct decision that they decided to remove it though.

xianshenglu commented 6 years ago

Why would we need this which can be found viewport-relative-lengths:

However, any scrollbars are assumed not to exist.

This will make 100vw include the width of scrollbar. I think we don't need this.

So,when we set width:100% or width:100vw on html, they both mean that the width of html should be 100% of the width of initial containing block according to the specs. However, width:100vw will get different behavior because of the word above.

ByteEater-pl commented 5 years ago

If the viewport-relative units are to be defined as independent of whether there are scrollbars, it's much more useful to always subtract a default scrollbar width (or rather even double, accounting for possible scrollbar-gutter). This way there isn't overflow when a single dimension in the layout or a sum thereof reaches 100 of the relevant units. And if there are no scrollbars, some stuff may be smaller, but that's not as big a deal (although authors may want to stretch or repeat backgrounds to cover the extra space).

It makes the most important use case easier when (actually if) the relevant environment variable arrives by not requiring its use along with inelegant calc expressions and possible at all for the near future.

coreyworrell commented 5 years ago

@tabatkins Not to beat a dead horse, but I do think this is important to get right. I just don't understand why viewport units aren't handled exactly the same as percentage units, as if the element is a direct child of the body for example.

<html style="overflow-y: scroll; /* avoid page shift when scrollbar appears */">
  <body style="margin: 0; padding: 0;">
    <div style="width: 100%;">This is 100% wide, looks good, yay!</div>
    <div style="width: 100vw;">WTF, horizontal scrolling now?</div>
  </body>
</html>

That example alone makes no sense to me. The purpose of viewport units was to be able to reference a percentage of the width of the "initial containing block", instead of only being tied to using a percentage of its parent element.

Why would one want to "draw" an element "under" the scrollbar and force a horizontal scrollbar? If you absolutely needed that for whatever reason, that should be the exception, and handling it would simply involve hiding the overflow however you see fit (overflow-x: hidden or overflow-y-hidden).

I have read through the IRC discussions and do not see any solid reason this was dropped.

tabatkins commented 5 years ago

If viewport units act like %s, then they change from being "resolveable at computed-value time" to "resolveable at used-value time". This is a huge change! It means that a lot of uses you might want simply don't work; any place that currently has "% acts like auto" behavior would give viewport units the same behavior.

We thought it was more useful to have viewport units always be usable.

coreyworrell commented 5 years ago

@tabatkins Sorry, I don't think I follow. Maybe my use of exactly was too broad. I just meant in the aspect of the size that the unit represents.

A viewport unit should always be usable like you said, of course. The definition of "viewport" is the issue though. To website developers, it should mean 1% of the available space I'm given. If a browser draws a scrollbar in that space, it is no longer available, thus the value of 1vw should adjust accordingly.

geryogam commented 12 months ago

Could we reopen this issue? The current viewport units are obviously useless as https://github.com/w3c/csswg-drafts/issues/1766#issuecomment-460470368 and the number of developers complaining show.

tabatkins commented 11 months ago

The previous resolution in this issue has been reversed in #6026. overflow: scroll on the root element (or similar things, such as scrollbar-gutter: always) will now reduce the size of the viewport units.

There's a few wrinkles - setting overflow:scroll on the body element will also trigger root scrollbars, but won't change viewport units, because of cyclic issues. And if you manually adjust the size of scrollbars with the WebKit/Blink scrollbar pseudo-elements, that won't be taken into account, again for cyclic reasons. But scrollbar-width on the root element will be taken into account, so thin scrollbars will work correctly.