Closed frehner closed 3 years ago
Another article that advocates for avoiding vh units completely because of this problem: https://chanind.github.io/javascript/2019/09/28/avoid-100vh-on-mobile-web.html
Agenda+ to make everyone read the article linked above. This is bad and we should do something about it; I'm less sure about what.
Is this behavior ever useful? Should it not be the case that if the size of the viewport changes, by way of scrollbars or due to browser chrome appearing, the size of the initial containing block changes. Or is there something special about this URL bar?
This is bad and we should do something about it; I'm less sure about what.
Agreed! As noted, my proposal doesnât really solve the issue, but it does at least offer choice and perhaps a better default than how vh currently behaves.
But if someone comes up with a solution that makes the vh unit itself useful again I would be all for it!
Should it not be the case that if the size of the viewport changes, by way of scrollbars or due to browser chrome appearing, the size of the initial containing block changes.
Thatâs actually how vh units initially behaved! But it turns out that itâs a horrible UX because content would actually move/change size as you scrolled. So Safari (and later Chrome) both agreed that just fixing it to a single height would be the better UX - feel free to take a look at the two citations in my first paragraph.
I'm thinking that the issue is that we really need a good default for vh
as it stands that doesn't cause data loss, because a lot of web developers do not test very comprehensively on mobile devices, instead they will drag their window small or use RWD browser tools that cause a small viewport. These methods won't highlight the appearing chrome problem you describe. It would be better to have the default for vh
be unable to cause data loss even if that caused content to shift, and then add the thing people can opt into that does the other behavior. As the people opting in then presumably know what they are doing. However maybe that ship has sailed.
I'm thinking that the issue is that we really need a good default for vh
Agreed; I would love for vh to be useful again.
However maybe that ship has sailed.
Yeah, I think it has unfortunately. There have been issues opened for both browsers for years and all have been closed saying the current behavior is intentional.
vh
doesn't pay attention to scrollbars, so that it can be a computed-value time unit and not depend on layout.
But nothing stops vh
from responding to UA chrome hiding/showing. That's not a content-based decision, it just happens as a result of user interaction. It means we have to invalidate as the chrome disappears, but otherwise is not problematic. I think we can just clarify that the viewport units should do so.
Further question: should it respond to on-screen-keyboard showing/hiding? I suspect the answer is no, but I'm not sure what answer is most consistent.
should it respond to on-screen-keyboard showing/hiding?
I would say that yes, any feature for adjusting to URL bar show/hide behavior should also include the keyboard and any other pop-up/pop-over browser widgets that cover part of the layout viewport. Example use case: if the UI consists mostly of a large text box, it's nice to make that fill most of the window. It's less nice if the box gets covered up by the keyboard you're using to type in it, and you end up with a scrolling text box inside a scrolling window.
An alternative to adding new units would be to expose environment variables for the size of the obscured region on each side of the screen. Or maybe, include this type of obscuring in the "safe inset area" variables that are already defined.
I think we can just clarify that the viewport units should do so.
sorry, can you clarify what you mean by this? Are you proposed that vh
units go back to the original behavior of changing the value of 1vh
based on UA chrome?
If so, I don't think that is a better solution than the current situation -- that's how it behaved originally and it was a worse experience than what we have now. :)
(If not, sorry, can you explain what you mean? I apologize)
An alternative to adding new units would be to expose environment variables for the size of the obscured region on each side of the screen. Or maybe, include this type of obscuring in the "safe inset area" variables that are already defined.
I think that would put us back at the original behavior of vh
units, which means that content actually changes size (and thus can shift underneath the user) when you scroll, right? Or am I misunderstanding your proposal?
If so, I don't think that is a better solution than the current situation -- that's how it behaved originally and it was a worse experience than what we have now. :)
Hm, I'm confused then. In what situations would it be worse? Naively, I'd think that it's the best of both worlds, neither under- nor over-flowing regardless of how the chrome is displayed.
I think that would put us back at the original behavior of vh units, which means that content actually changes size (and thus can shift underneath the user) when you scroll, right? Or am I misunderstanding your proposal?
Ah, hm, is this the issue? It's really better to have something over or underflowing than have things possibly shift when the user scrolls?
Ah, hm, is this the issue? It's really better to have something over or underflowing than have things possibly shift when the user scrolls?
Correct! At least, that's what Safari and Chrome teams agreed as well. Here's a brief history of vh units:
vh
units are added to browsers. 1vh
changes values depending on UA chrome sizes. This is a bad UX because content is constantly shifting as a user scrolls up and down the page.
Safari changes the value of 1vh
to be whatever the height when UA chome is minimal. Here's a quote on their reasoning, around 2015:
This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)
The base problem is this: the visible area changes dynamically as you scroll. If we update the CSS viewport height accordingly, we need to update the layout during the scroll. Not only that looks like shit, but doing that at 60 FPS is practically impossible in most pages (60 FPS is the baseline framerate on iOS).
It is hard to show you the "looks like shit" part, but imagine as you scroll, the contents moves and what you want on screen is continuously shifting.
Dynamically updating the height was not working, we had a few choices: drop viewport units on iOS, match the document size like before iOS 8, use the small view size, use the large view size.
From the data we had, using the larger view size was the best compromise. Most website using viewport units were looking great most of the time.
Chrome follows suit about a year later
So, that's how we got here -- vh
units used to change dynamically, then it was determined that was horrible (and honestly it was), but now we're left with a vh
value that only reflects the larger view size but not the small view size, which makes full screen designs difficult to work with.
Ah, I hadn't realized you wanted vhc
to always represent the minimum viewport height (minus the pop-up chrome), even when that chrome isn't current visible.
So there are three distinct use cases:
Ah, I hadn't realized you wanted
vhc
to always represent the minimum viewport height (minus the pop-up chrome), even when that chrome isn't current visible.So there are three distinct use cases:
- A stable layout that exactly fits the full viewport with minimized chrome.
- A stable layout that exactly fits the viewport when pop-up browser chrome is displayed.
- A dynamic layout that always exactly fits the current viewport, regardless of how many browser widgets are surrounding it.
Seems like a great summary, yes! Thank you.
I think everyone hoped that one css unit (vh
) could work for all those cases (and honestly it would be awesome if it did), but I'm not entirely sure what that would look like or how it would work.
So I proposed an additional unit instead :D
* A stable layout that exactly fits the viewport when pop-up browser chrome is displayed.
If the vhc
means this, it would be confusing. vhc
sounds the height includes the browser chrome's height.
Also, I guess the units people really wants is the visual viewport size instead of the layout viewport. CCing @bokand
I initially argued that vh should reflect the minimum possible size but didn't get a response from WebKiters so decided that the larger size and compat with Safari was preferable to a saner default but incompatibility with Safari.
Also, I guess the units people really wants is the visual viewport size instead of the layout viewport. CCing @bokand
I don't think so. The visual viewport is conceptually detached from layout. i.e. if the user zooms in, the visual viewport shrinks. So we don't ever want layout to depend on visual viewport properties.
I think having a new unit for the minimum possible size sounds fine, or exposing the browser chrome insets instead as @AmeliaBR mentioned (I think that's equivalent to this proposal) which might be easier to explain.
I would say that yes, any feature for adjusting to URL bar show/hide behavior should also include the keyboard and any other pop-up/pop-over browser widgets that cover part of the layout viewport. Example use case: if the UI consists mostly of a large text box, it's nice to make that fill most of the window. It's less nice if the box gets covered up by the keyboard you're using to type in it, and you end up with a scrolling text box inside a scrolling window.
Chrome on Android today resizes the ICB when the keyboard comes in. I think it's a rather regrettable decision since resizing the ICB can take a long time - it's a significant source of making the mobile web feel janky. In addition, it's not uncommon to tap on an editable only for the resized layout to now obscure the box you're trying to type into.
We've had success on ChromeOS using a model where the onscreen keyboard shrinks the visual viewport but leaves layout unchanged. Anecdotally this works much better: the user can still pan around the full viewport but the layout doesn't change at all when the keyboard comes up which works much better, especially on sites that didn't give much thought to onscreen keyboards. Unfortunately it's a difficult thing to bring to Android because of compat so we haven't been able to do it yet.
or exposing the browser chrome insets instead as @AmeliaBR mentioned (I think that's equivalent to this proposal) which might be easier to explain.
My only concern with that is: does the size of the inset change as the UA chrome changes size? If so, that feels like we would actually just be back at the initial behavior of vh units, and we wouldnât have solved anything. But if they remain the same value even as UA chrome resizes, then I think thatâs a good alternative to a vhc unit
though thinking about it, I'm curious how it would work. If there's 1 value that changes, then that's not good.
If there are 2 values (e.g. something like inset-ua-chrome-small
and inset-ua-chrome-large
), what would it take to get a full-height element?
For example:
height: 100vhc;
vs
height: calc(100vh - env(inset-ua-chrome-small) + env(inset-ua-chrome-large));
If I'm understanding the proposed env variable correctly - but it's entirely possible I'm not, though.
My recommendation would be the inset is a "collapsible height", i.e. how much the UA might expand as a result of scrolling.
In this case, this would be the full URL bar height, regardless of whether that's showing. So the full height would be:
height: calc(100vh - env(inset-collapsible-height))
Ah ok, yeah, thatâs not too bad. Personally I would prefer to use a vhc unit (easier understand and teach, and if you have to use that value itself in a calc then it is much simpler than having to do nested calcs - unless you then put it in an custom property or something) but I wouldnât be opposed to it if thatâs whatâs decided is better.
I also think youâll find more developers using vhc (or equivalent) over vh so it would be nice if it were simple, but I have no hard evidence of that -- except for all the articles that say "avoid vh
on mobile devices" :P
I was talking in slack with someone, and they proposed an alternative idea.
What if the behavior of vh
could be determined in a dynamic way, similar to box-sizing
? For example
.container {
vh-sizing: expanded-viewport-height;
height: 100vh;
}
with vh-sizing
options potentially being something like
collapsed-viewport-height
(the current behavior of vh
)expanded-viewport-height
(the desired behavior of a vhc
unit)exact-viewport-height
(a unit that would always match the viewport exactly, even with keyboards or other UA chrome)(all of these are just placeholder names)
With this pattern, the developer has options for all three use cases as noted by @AmeliaBR here, while also still being able to use vh
in a backwards-compatible way with "progressive enhancement" (probably not the right word for it but I hope the idea comes across).
I thought it was a novel enough idea, and the backwards and forwards compatibility aspect of it made it interesting enough for me to add here for feedback as well.
My concern here is that as long as vh
itself is defined to be too big when browser chrome is present, sites developed on desktop using emulation or just narrow window sizes will be designed using the existing viewport units, and not work correctly on mobile. Critical information will be beneath the "fold" in these cases. I agree that the vh
unit needs to be stable as the user browses the page, but it seems to me that it would be safer (more consistent with the âavoid datalossâ principle) if the vh
unit was tailored to the more conservative size, that corresponds to the size on initial page load. If we then also need a unit that corresponds to the fully-retracted UI's viewport size, then we can add an additional unit for that; but imo the default behavior should be the safer one if possible.
I agree that the vh unit needs to be stable as the user browses the page, but it seems to me that it would be safer (more consistent with the âavoid datalossâ principle) if the vh unit was tailored to the more conservative size, that corresponds to the size on initial page load. If we then also need a unit that corresponds to the fully-retracted UI's viewport size, then we can add an additional unit for that; but imo the default behavior should be the safer one if possible.
I agree; I think @bokand also mentioned that they (blink) would have preferred that to be the default here. Unfortunately, webkit adopted the opposite and now it's standard.
I think in an ideal world we could change the default behavior of vh
, but then you have the potential to break the layout of tons of websites that relied on the old (current) size of vh
. In other words, it isn't a 100% backwards compatible change.
Which is why I find the solution I listed above compelling, because it is 100% backwards compatible while also allowing new functionality for devs that opt-in to the new behavior.
The CSS Working Group just discussed Add vhc value
.
I thought it might be useful to write down some of the pros/cons of each proposal so far.
vhc
Pros:
Cons:
Progressive enhancement usage, and a codepen example
.container {
height: 100vh;
height: 100vhc;
}
env(inset-collapsable-height)
Pros:
Cons:
vh
willProgressive enhancement usage, and a codepen example
.container {
height: calc(100vh - env(inset-collapsable-height, 0px));
}
vh-sizing
Pros:
vh
unit but changes its behavior where supportedCons:
vh
definitions in the same blockProgressive enhancement usage, and a codepen example
.container {
height: 100vh;
vh-sizing: expanded-viewport-height;
}
Hopefully this is helpful. Let me know if you want me to change/add something to this list as well.
- Doesn't appear to have an easy way to do progressive enhancement, see this example - (I tested in firefox)
FWIW height: calc(100vh - env(foo, 0px));
does work. That's just because calc(100vh - 0)
is invalid, because 0
in calc gets parsed as a <number>
instead of a <length>
, and you can't subtract <length>
and <number>
s inside calc.
That may be something worth special-casing / changing to make work? Not sure.
- Doesn't appear to have an easy way to do progressive enhancement, see this example - (I tested in firefox)
FWIW
height: calc(100vh - env(foo, 0px));
does work. That's just becausecalc(100vh - 0)
is invalid, because0
in calc get's parsed as a<number>
, not a<length>
, and you can't subtract<length>
and<number>
s.That may be something worth special-casing / changing to make work? Not sure.
For clarity, I want these styles to not apply in cases where the browser doesn't understand them, and the problem is that the browser actually does try to apply them even if it doesn't understand them.
For example, if you look at the height: 100vhc
example in the browser dev tools, you'll see it's crossed out because the browser doesn't understand it. That's what we want -- browsers that don't understand the unit to not apply the unit.
But in the env example, the browser thinks it understands it and tries to apply it, which is actually what we don't want in this case.
If that makes sense?
Well, the way to deal with the variable not being there is setting the fallback value, just like custom properties.
That being said https://github.com/w3c/csswg-drafts/issues/3285 would've probably been the model you want, though env()
was shipped without much public discussion... :)
FWIW height: calc(100vh - env(foo, 0px)); does work. That's just because calc(100vh - 0) is invalid, because 0 in calc get's parsed as a
, not a , and you can't subtract and s.
I can't parse this; do have too many, or not enough, negations?
Well, the way to deal with the variable not being there is setting the fallback value, just like custom properties.
That being said #3285 would've probably been the model you want, though
env()
was shipped without much public discussion... :)
I think the point Iâm making is being missed - it doesnât work for progressive enhancement situations, as shown in the code pen. Even with the fallback value for the env value
FWIW height: calc(100vh - env(foo, 0px)); does work. That's just because calc(100vh - 0) is invalid, because 0 in calc gets parsed as a
<number>
instead of a<length>
, and you can't subtract<length>
and<number>
s inside calc.
Why is env(foo, 0px)
equivalent to 0
?
Well, the way to deal with the variable not being there is setting the fallback value, just like custom properties.
That being said #3285 would've probably been the model you want, though
env()
was shipped without much public discussion... :)
Whoops, my apologies, I was wrong. I've updated the codepen and the pros/cons list to show the progressive enhancement for that as well. Thank you for correcting me.
Is there anything I'm missing or can do to help out?
https://twitter.com/thekitze/status/1196709498670460928?s=20
100vh on iOS will be the end of me as a web developer
A post showing sentiment about this issue.
Happy new year!
@jensimmons is there anything I can do to help move this along? I've summarized a list of the current proposals with pros and cons if that helps.
I'm totally willing to do more or help remove any roadblocks that are in my power to remove to help out as well.
Anything to make vh units usable on mobile devices again :)
The CSS Working Group just discussed New viewport unit
, and agreed to the following:
RESOLVED: Add a set of viewport units (vhc for ex.) that reflect the size of the layout viewport less all UA UI
Sorry, forgive my ignorance - was the conclusion of the meeting detailed above to move forward or table this proposal for the time being? I'm not sure if the tabling discussion was around this specific proposal or around other things related to it.
We're moving forward - we resolved to add new static 'safe' units that will let you lay things out and be guaranteed to fit when all of the browser chrome is present. The part we did not decide on is whether to do anything about the dynamic cases, when chrome is appearing or disappearing.
We're moving forward - we resolved to add new static 'safe' units that will let you lay things out and be guaranteed to fit when all of the browser chrome is present. The part we did not decide on is whether to do anything about the dynamic cases, when chrome is appearing or disappearing.
Thank you!
I noticed in the linked bugzilla that there might be some bikeshedding on the name itself (which I totally agree with -- vhc
was just a placeholder name I proposed).
Do we need to have that conversation here or will that happen in a separate meeting/location?
A conversation here would be fine. I expect this issue will stay open until edits are made on a draft, then it may get closed and a new "should we change the name" issue might get created. If we can close on a name before the edits are made, then all the better.
Removing the tag for css-values-3
as this is new work and should go in css-values-4.
css-values-3` is stable, being prepped for Proposed Rec.
Looking at naming possibilities here, just throwing some out:
vhmin
- aka the smaller vh unit.
vmin
vhm
? or minvh
?vhc
- as originally proposed: "view height with chrome."
vhc
- view height collapsed.
More posts about potential names:
@Crissov in this comment
@JoshuaLindquist in this comment
@bmakuh in this comment
I'll continue to update this comment if/when more are proposed, so that you can find all the name suggestions in one place.
On a side note - is there anything that this spec might do/change to the vmin
and vmax
units that are part of css-values-3?
On a side note - is there anything that this spec might do/change to the vmin and vmax units that are part of css-values-3?
No, those are widely-implemented and not changeable.
On a side note - is there anything that this spec might do/change to the vmin and vmax units that are part of css-values-3?
No, those are widely-implemented and not changeable.
Followup question - do you think that there should be equivalent values created for this new unit? e.g. vcmax
vph
/vpw
â viewport height/widthph
/pw
â page height/width,sh
/sw
â spread height/width,h
/w
, height
/width
â height/widthx
/y
are unavailable, because x
is already used as a <resolution>
unit) Those names are great @Crissov , way better than anything I came up with. If it's ok with you, I'll add them to the list in my comment and cite you + link your comment. That way it's easy to find all the name suggestions in one place.
Sure, go ahead.
Thinking about it from a block/inline perspective instead (and taking some inspiration from Crissov) to make the new unit a relative of vi
and vb
:
bp
: Block page
ip
: Inline page
bs
: Block spread
is
: Inline spread
bc
: Block collapsed
ic
: Inline collapsed
Taking a phrase from the spec, could it explicitly be the "containing block height" and "containing block width"? I'm not sure how common those phrases are; this could be confusing.
cbh
: Containing Block Height
cbw
: Containing Block Width
Or using "x" as short for "axis":
ix
: Inline Axis
bx
: Block Axis
I'm happy to see this evolving towards a solution, thanks @frehner for the hard work, and everyone for the interesting discussions.
I did open the bug in Webkit's Bugzilla 5 years ago, and I've had a lot of (frustrated) feedback in my webmentions too.
@jensimmons if you need actual use cases, I might be able to contribute:
My use case, visible online is a game that uses vh
, vw
and even vmin
, and combines them (there is even a width: calc(100vw - 6vmin - 74.4186vh);
in my CSS đ
) to size and position SVG elements in the responsive UI.
This UI is meant to be "full viewport" (full screen would be better, but not always possible), everything always visible, and should not require scrolling, the browser chrome being minimized or not.
I've been using a JavaScript library to "fix" the issue for years, but would definitely prefer a CSS only solution.
I'll be happy to help if possible, using the new value in a branch of my game code as soon as it's available in the first browser engine.
Latest info
Added
lvh
,svh
,dvh
,lvw
,svw
, anddvw
units.See https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-863677668 and https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-880040513 for more details. đ
Original content below:
Background
vh
is defined asEqual to 1% of the height of the initial containing block.
Perhaps the current behaviour 1 2 could also be defined asIn other words, on devices where the browser chrome changes size (e.g. mobile devices),
100vh
is actually larger than the viewport when the browser chrome is maximized, and thus overflows.A brief history of the
vh
unit is outlined here https://github.com/w3c/csswg-drafts/issues/4329#issuecomment-542420036Proposal
vhc
(c = "with chrome", but it could be something else) could be defined asOn devices without a changing chrome size (e.g. desktop devices),
1vh === 1vhc
.Advantages and Drawbacks
Pros and Cons list for each proposal
Original content of this section:
A drawback of
vh
units is that content will be cut off when (1) you load a page and (2) when you scroll upwards, while the content fits when you scroll down and the chrome is minimized.The
vhc
unit would be the inverse: content would fit the page when (1) you load the page and (2) when you scroll upwards, but you would see additional content (or whitespace, depending on the implementation) when the chrome is minimized.It also would provide a better experience than
vh
for games and other full-screen content that doesn't or shouldn't scroll at all.In the end, this proposal does not completely solve the issue of
1vh
needing to be different values at different times. However, it does at least give the developer a choice in which value that they want to use.(For what it's worth, my personal preference is that I would end up using
vhc
units for responsive designs, because I would rather have additional content visible or some whitespace added, rather than have content cut off and not visible.)Alternative Proposals
And a breakdown of the pros and cons of each proposal so far
Current Workarounds
As it stands, web developers that want to have a full-height website are either reliant on javascript 1 2 3 to get
vh
units to not cut off content, or just tend to avoid 1 usingvh
units altogether.Unrelated
This is my first time proposing, so if I did something wrong or need to improve something please let me know! Thank you for your patience. :)