w3c / csswg-drafts

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

[css-values-4] Clarify how and when the initial containing block and visual viewport may deviate #5777

Closed mind-bending-forks closed 10 months ago

mind-bending-forks commented 3 years ago

Background

This proposal is supported by the following article: https://mind-bending-forks.github.io/css-viewport-percentage-length-units/

Mobile browsers implement a toolbar hiding/revealing feature that results in the visual viewport changing size and position. Having a dynamically resizing initial containing block may lead to unsightly reflow, and so current design avoids this. An inevitable consequence is that the initial containing block deviates from the visual viewport in the initial un-zoomed state (zero pinch-zoom), and this results in content that would ordinarily be visible being truncated while the toolbars are in view.

An issue arises because of the way the hide/reveal mechanism has been linked to scrolling of the underlying content. There are certain situations, such as when using viewport-percentage length units to create content that fills the viewport and when the content contains sub-regions that are scrollable, where the toolbars (and so truncated content) persist. In the problem cases, this truncation persists indefinitely, or until a user scrolls/swipes the outermost content, and so only if they scroll/swipe the outermost content, and so only if they can scroll/swipe the outermost content (which is not currently always the case).

This proposal seeks to place limitations on content truncation by clarifying that the initial containing block and visual viewport are permitted to deviate, but only briefly/temporarily and for a specific action/purpose, otherwise the initial containing block should be re-aligned with the visual viewport.

This therefore provides guidance to browser developers that may encourage modifications to current design that address or minimise the issues identified above. My hope is that problems with truncation of content currently marked as wont fix will at some point be changed to "let's see what we can do to improve matters" at least.

Proposal

The proposal is for the following text (or some variation on it) to be included in the CSS specification, probably alongside the definition of the initial containing block - which is currently defined at https://www.w3.org/TR/CSS21/visudet.html#containing-block-details:

At zero pinch-zoom, the user agent may allow the initial containing block (ICB) and visual viewport to diverge (e.g. by overlaying or shifting the ICB), provided that the change is:

  • associated with the display of additional user interface required to undertake a specific action, such as a keyboard when assistance with form input is required or an address bar when the user needs to navigate or to a new location, and
  • temporary by design such that:
    • the user interface only appears when required or requested by the user, and
    • the additional user interface is removed when the user completes the action, dismisses it or undertakes an unrelated action.

Any other change must result in a realignment of the ICB with the visual viewport and a corresponding change of viewport-percentage length units.

Advantages

Drawbacks

The drawbacks could be overcome by introducing visual-viewport-percentage units (vvh, vvw) that are calculated relative to the instantaneous visual viewport size. It would then be the responsibility of the web developers to check how well their particular usage of visual-viewport-percentage units works.

Alternative and Related Proposals

Current Workarounds

bokand commented 3 years ago

Just to clarify a point of terminology, from the linked article (which is very well put together, by the way - thanks!):

Aside: I don't know about anyone else, but I find use of the word "initial" in "initial containing block" slightly confusing. To me, initial tends to imply that the quantity is calculated at the start (e.g. on page load) and that it is static, whereas it is very much dynamic, adapting to changes in scroll, pinch-zoom, and adapting to changes in the visual viewport size (e.g. if the browser window is rotated or resized.)

What you're describing as "Initial Containing Block" here is actually what we usually call the "Layout Viewport". The initial containing block as defined in CSS2 is a layout-only concept and conceptually doesn't ever change position, even when you scroll.

The "layout viewport" isn't specified but the behavior is fairly interoperable - it is a viewport in the sense that it's position changes as the user scrolls. Its size is by definition the size of the visual viewport at minimum scale. Position: fixed elements use this as their containing block - meaning they resolve percentage based sizes from this (hence, a position: fixed; height: 100% element will resize in response to the URL bar hiding) as well as position (position:fixed; bottom: 0 sticks to to viewport bottom even as the URL bar hides).

So, if I understand correctly, your proposal is the have the layout viewport and ICB always match (allowing some temporary disagreement).

Is this not just going back to the way things were when hiding and showing the URL bar would resize the ICB? We changed to a model where the ICB isn't resized since experience showed it made many common use cases for vh unusable. I don't see how this proposal addresses the concerns that led to that change in the first place.

There are no issues with regards to backwards compatibility. Web developers do not need to change anything.

I don't think this is true. The current behavior has been in place and interoperable for the last few years at least (in addition to the problem sites we had prior to the change). Any page that, for example, sets font size based on vh will now have a UX destroying reflow on each change in scroll direction anytime the URL bar is shown/hidden.

I think what you're suggesting is that there are use cases where a page actually wants this dynamic, resizing layout viewport as the source for vh units. I believe that's true but using this as a default behavior for vh (introduced before mobile URL bars were around) hasn't worked in the past.

Assuming you can't build your layout on position: fixed, which would get that behavior (but I can imagine comes with other compromises), I would suggest something akin to (another?) vh-type unit that is dynamic (or something like a vh-sizing property that allows dynamic but defaults to the current behavior). That way authors would have to opt-in to the new and unambiguously defined behavior.

Regarding more general URL bar behavior and design in browsers - I would point out this is an extremely constrained and complex area. The URL bar is the primary security surface for mobile browsers and the most user visible piece of UI. Making changes to this is extremely difficult; much more so than how the URL bar interacts with layout and page APIs (which is merely "very" difficult).

mind-bending-forks commented 3 years ago

Thanks @bokand. There's a lot said here. I suspect that quite a bit of it is talking at cross-purposes or misunderstood, mainly because of my lack of a grip on the terminology. So, let's see if we can address that first, and maybe I can produce an updated article correcting what needs to be corrected and we can go from there.

What you're describing as "Initial Containing Block" here is actually what we usually call the "Layout Viewport". The initial containing block as defined in CSS2 is a layout-only concept and conceptually doesn't ever change position, even when you scroll.

Gah... You're right. Every time I think I've understood what the Initial Containing Block is, it turns out I haven't! The thing that I've referred to as the Initial Containing Block isn't. I think the main issue is that it is defined as being pinned to the document origin, whereas I've got mine shifting around in the article. How about its size? To keep things simple, let's consider the case where there are no toolbars that might hide/reveal themselves complicating matters.... Does the ICB change size, and if so, in response to what? I presume it changes size in response to viewport resize (e.g. change from portrait to landscape on mobile, or browser window resize on desktop.) Does it also expand on pinch-zoom on mobile?

Once I've got ICB properly pinned down, we can come onto Layout Viewport (if you don't mind taking the time to educate me?!) Thanks.

bokand commented 3 years ago

So, let's see if we can address that first, and maybe I can produce an updated article correcting what needs to be corrected and we can go from there.

:+1:

Does the ICB change size, and if so, in response to what? I presume it changes size in response to viewport resize (e.g. change from portrait to landscape on mobile, or browser window resize on desktop.) Does it also expand on pinch-zoom on mobile?

On desktop the ICB is very simple; it changes size only when you resize the window. Here, the layout viewport will always be the same size as the ICB. (ICB size doesn't ever change in response to pinch-zoom).

Mobile is where things get complicated.

On mobile, the ICB can change size if the window is resized, via rotation or with the on-screen-keyboard appearing (though I think this varies by browser). It doesn't change size from the URL bar coming in and out.

However, its size can be disconnected from device dimensions using the <meta name="viewport"> tag. When you specify width=x in this tag, you're specifying the size of the ICB. Most mobile-friendly sites specify width=device-width which means the ICB will match the device dimensions. In this case, assuming a well-behaved layout (see below for alternatives), the ICB and layout viewport will more or less be the same size (aside from the URL bar behavior).

However, things get extra wonky in the edge cases. Three important points:

This means authors can explicitly, and separately, control the size of both the ICB and the layout viewport. Here's an illustrative example:

  <meta name="viewport" content="width=1000,minimum-scale=4">
  <style>
    html {
      width: 250px;
    }
  </style>
  <!-- Assume content doesn't overflow <html> -->

Loaded on a mobile device with a 500px screen width we get:

ICB = 1000px
Layout Viewport = 125px (since minimum scale is 4, `500px/4 == 125px`)

If we remove the minimum-scale from the <meta> tag we'd get

ICB = 1000px
Layout Viewport = 250px (since the computed minimum-scale now comes from content width which so is 2)

However, if we now add <body style="width=2000px">, the content becomes 2000px wide so minimum-scale becomes 0.25:

ICB = 1000px
Layout Viewport = 2000px

All this to the point: the layout viewport and the initial containing block, despite being mostly equivalent in typical, well-behaved cases, can actually vary quite widely. If I remember correctly, vh is calculated relative to the ICB (with addition from URL bar height).

theres-waldo commented 3 years ago

This proposal is supported by the following article: https://mind-bending-forks.github.io/css-viewport-percentage-length-units/

@mind-bending-forks, in the example pages in sections 2.2 and 2.3 of this article, is the content truncation problem you describe resolved if you use 100% instead of 100vh as the size of the <body> element?

frehner commented 3 years ago

This proposal is supported by the following article: https://mind-bending-forks.github.io/css-viewport-percentage-length-units/

@mind-bending-forks, in the example pages in sections 2.2 and 2.3 of this article, is the content truncation problem you describe resolved if you use 100% instead of 100vh as the size of the <body> element?

that should work in that simple case, but it doesn't solve the need for having an accurate value for vh - I may want a div that is deep in my HTML tree to fill the screen 100% but that would require me to make sure that each ancestor of that div is also 100% otherwise it won't work. Thus the value of (accurate) vh units that don't require developers to do that.

theres-waldo commented 3 years ago

@mind-bending-forks, in the example pages in sections 2.2 and 2.3 of this article, is the content truncation problem you describe resolved if you use 100% instead of 100vh as the size of the <body> element?

that should work in that simple case, but it doesn't solve the need for having an accurate value for vh - I may want a div that is deep in my HTML tree to fill the screen 100% but that would require me to make sure that each ancestor of that div is also 100% otherwise it won't work. Thus the value of (accurate) vh units that don't require developers to do that.

I understand that. My point is, if 100% solves the issue on that simple page, then the proposed vhc units should solve the issue more generally (and conversely, if the behaviour with 100% on the simple page is still unsatisfactory for some reason, the behaviour with vhc is likely to be as well).

mind-bending-forks commented 3 years ago

I can't keep up with you all. Every time I get my head around a post and am drafting a reply, another one appears!

In reply to @bokand. So things are complicated by the fact that each browser is slightly different in its implementation. That said, in summary, if you consider the simpler case where there are no hide/reveal toolbars, I think following appears to be true of the ICB based on what you have said:

Is that more-or-less correct?

It's the last bullet which is most surprising/puzzling to me. The vh units are currently defined to be equivalent to 1% of the ICB. When I pinch zoom on the basic 100vh example or this additional 2nd 100vh example, the size of the top and bottom and zones - which are each 10vh grow, as does the 100vh body. This must mean that vh is increasing as I pinch zoom, which must mean that the ICB is increasing in size as I pinch zoom. Am I missing something?

EDIT: Just realising now that your comment about the ICB not ever changing in size in response to pinch-zoom was probably only in reference to desktop. Whereas on mobile, as I observed, it does change in response to pinch zoom (potentially with limits on zoom set by the viewport <meta> tag or CSS). If I've got that pinned down, we can move on to the meaning of Layout Viewport and I'll be back with some questions!

In reply to @theres-waldo I've uploaded some additional examples so you can test for yourself. There's a

In my tests of these on Android, there are some issues/differences, but not with truncation of content. If anything it's the opposite:

I don't have an iOS device to test on right now I'm afraid.

theres-waldo commented 3 years ago
  • You say it doesn't ever change size in response to pinch-zoom.

It depends on your frame of reference :)

Pinch-zooming changes the ratio of CSS pixels to screen pixels. The ICB size remains constant in CSS pixels, and varies in screen pixels, as you pinch-zoom.

theres-waldo commented 3 years ago
  • On Chrome for Android, 100% corresponds to the the initial height of the viewport with the address bar. Here however, the address bar persists and cannot be hidden. So, this works in the sense that the content fills the available viewport and the scrolling seems to work as it should too. Pinch-zoom works as you would expect.

This is the behaviour I had in mind for 100%. To the extent that Firefox doesn't behave like this, those are bugs we should fix in Firefox.

The question is: are you happy with this behaviour as a page author?

mind-bending-forks commented 3 years ago
  • You say it doesn't ever change size in response to pinch-zoom.

It depends on your frame of reference :)

Pinch-zooming changes the ratio of CSS pixels to screen pixels. The ICB size remains constant in CSS pixels, and varies in screen pixels, as you pinch-zoom.

Thanks @theres-waldo. Wow! I didn't realise I'd need to be Einstein to get my head around positioning a few rectangles 😀 That's a lot of degrees of freedom for my brain to cope with, so please give me some time to digest that.

  • On Chrome for Android, 100% corresponds to the the initial height of the viewport with the address bar. Here however, the address bar persists and cannot be hidden. So, this works in the sense that the content fills the available viewport and the scrolling seems to work as it should too. Pinch-zoom works as you would expect.

This is the behaviour I had in mind for 100%. To the extent that Firefox doesn't behave like this, those are bugs we should fix in Firefox.

The question is: are you happy with this behaviour as a page author?

Regarding Chrome's behaviour, I'll have bit more of a play to be sure, but for this specific holy-grail-type example, yes, it appears I am happy! It would be nicer if there was a way to trigger the address bar to hide and to consume the whole space potentially available as is normally possible, but this is only a minor inconvenience that I'm not that bothered by. Far more important is the fact that the layout (by which I mean header and footer positioning in relation to viewport) and behaviour (scroll) are both consistent across desktop and mobile with no truncation of content. So, while this doesn't necessarily fix the issues with vh, it's a good workaround for this example as far as I am concerned. I'd be interested to know what Safari on iOS does with this.

mind-bending-forks commented 3 years ago
  • You say it doesn't ever change size in response to pinch-zoom.

It depends on your frame of reference :) Pinch-zooming changes the ratio of CSS pixels to screen pixels. The ICB size remains constant in CSS pixels, and varies in screen pixels, as you pinch-zoom.

Thanks @theres-waldo. Wow! I didn't realise I'd need to be Einstein to get my head around positioning a few rectangles grinning That's a lot of degrees of freedom for my brain to cope with, so please give me some time to digest that.

OK. I read an understanding css units tutorial and that has helped clear it up for me. I was thinking of changes in size relative to the device/real world. On pinch-zoom on mobile, the ICB expands in terms of physical size (device/screen pixels or inches), but is constant in CSS px as you say. So, 1px = x/96 in, where x is the zoom factor (that is, unless the user has changed the default device/CSS pixel ratio via <meta> or CSS.)

mind-bending-forks commented 3 years ago

My input to this thread will likely be minimal over the next week because I've got a lot on. I'd like to do a bit of testing to check my understanding, and then I'll likely be back with some requests for clarification before I update my article.

tabatkins commented 10 months ago

@mind-bending-forks Since you last posted to this issue, the CSSWG resolved (and engines implemented) three sets of viewports units: the large (with all temporary UI hidden), small (with all temporary UI shown), and dynamic (whatever UI is actually being shown at the moment) viewport units: https://drafts.csswg.org/css-values-4/#viewport-relative-lengths

I think the dynamic viewport units, in particular, address the concern you laid out in the OP. Do you agree? Is there anything else still causing you problems in this regard?

mind-bending-forks commented 10 months ago

Hi @tabatkins. Apologies for having gone quiet on this issue back in 2020. At the time, I was hoping to assist with finding a resolution but I found myself getting drowned by emerging personal and work matters and this is one of the things that I had to temporarily stop pursuing.

Thank you for alerting me to the CSSWG draft of the CSS Values and Units Module Level 4. I have reviewed my initial submission and associated supported document, to remind myself what I perceived the issues and potential solutions to be. I have also read the CSSWG draft text section on the new support for large, small and dynamic viewport units. I have also created dhv equivalents of the vh problematic examples I originally used, so see what impact that has:

Example 1:

Example 2:

Example 3:

In terms of what's written, the new units do indeed appear to deal with the issue I raised here!

In terms of the examples, I wasn't fully able to test this, in the sense of observing dynamic behaviour when the address bar disappears using dvh units. That's because on Firefox and Chrome on Android, the address bar becomes permanent for the dvh examples above (whereas, for the original vh examples, it is temporary and gets out of the way when at full scroll). No amount of scrolling would cause the address bar to be hidden for me. These are valid browser-specific implementation decisions though. The key thing is that the content is not unexpectedly or irretrievably truncated in those examples now (and that if the address bar were to become hidden, the content should adapt appropriately.)

So, I'm happy for this issue to be closed off now. Thank you!