Open flackr opened 2 years ago
@bramus @bokand FYI
I’ve given all proposals in here a lot of thought and while I do like the general idea behind Option 5 (PosFixedViewport). But, I have a few concerns to be fully convinced of it:
position: fixed
on all iOS browsers + Chrome on CrOS. This demo here – which mimics a bottom navigation bar – for example would no longer work as the author expected it to work. This will annoy authors.Could we solve this differently? Instead of overloading the position property with new values – such as device-fixed
, visual-viewport-fixed
, … – could we extend its syntax and provide the viewport as an extra argument? I’m thinking of:
position: fixed / layout
= current behavior, would be the same as position: fixed
)position: fixed / visual
= fixed against the visual viewport, also when zoomed inposition: fixed / fixed
(* lacking a better name) = the proposed behavior from Option 5 (PosFixedViewport)With this:
position: fixed
would not be changed, making the adjustments backwards compatibleEither way, I think we should definitely adjust Chrome on Android to no longer resize the ICB when the OSK gets shown. This can be implemented separately from the outcome here.
(Also see the Visual Viewport Units from https://github.com/w3c/csswg-drafts/issues/7194 that would play nice with position: fixed / visual
)
- This would alter the existing behavior of
position: fixed
on all iOS browsers + Chrome on CrOS. This demo here – which mimics a bottom navigation bar – for example would no longer work as the author expected it to work. This will annoy authors.
You proposed no longer resizing the ICB on Android (without this reinterpretation), which would have the opposite effect of annoying authors who are currently expecting the bottom bar to stay visible wouldn't it? We will be annoying some group of users when we align the keyboard behavior, right? Do you think that users who wanted the bottom / top bar sometimes obscured by the keyboard will be greater in number / more annoyed than the users who didn't? My expectation was that more devs would have expected fixed pos elements to stay visible (the Android behavior) and that changing the default fixed behavior to no longer do that would be more annoying.
Note that a developer could build the old behavior with navigator.virtualKeyboard.overlaysContent = true
when iOS adds support for this.
- Authors cannot easily feature detect wether the new or old behavior is being used. Therefore they cannot predict how the site will look with the OSK shown. This will annoy authors as well.
If we launch this change in coordination with no longer resizing the ICB, there will generally be no visual change for developers. Whereas if we don't, there will be. This does risk annoying developers on iOS / ChromeOS but they would also currently be annoyed by the Android behavior. The navigator.virtualKeyboard.overlaysContent
allows developers to design their own consistent behavior though it is admittedly not yet supported on iOS.
- This does not provide a solution for authors to stick something against the Visual Viewport, something that might come in handy for Floating Action Buttons or Toolbars which do need to stay above the zoomed content.
Presumably this is the device-fixed (option 3) in the above list, right? I.e. not changing with zoom. I do think that this is valuable. My main concern is that device-fixed is not the right behavior for bottom-bars, so even if we add it, it does not solve the bottom-bar issue. I do think device-fixed
may be independently valuable even though it does have accessibility concerns.
Could we solve this differently? Instead of overloading the position property with new values – such as
device-fixed
,visual-viewport-fixed
, … – could we extend its syntax and provide the viewport as an extra argument? I’m thinking of:
position: fixed / layout
= current behavior, would be the same asposition: fixed
)position: fixed / visual
= fixed against the visual viewport, also when zoomed in
To be clear, is this option 3 or option 4?
position: fixed / fixed
(* lacking a better name) = the proposed behavior from Option 5 (PosFixedViewport)
- The behavior of
position: fixed
would not be changed, making the adjustments backwards compatible
We will be changing the position: fixed behavior with the no longer resizing the ICB change, which is not backwards compatible on Android without an update to their behavior.
- Authors can feature detect the new behavior and act upon that
You could feature detect this when the keyboard is shown by comparing the height of 100% fixed to the viewport. Alternately the virtualKeyboard API lets developers opt into alternate treatments.
- Authors can also stick something against the Visual Viewport.
Agreed, this is an independently useful feature - but not the best for bottom-bars.
You proposed no longer resizing the ICB on Android (without this reinterpretation), which would have the opposite effect of annoying authors who are currently expecting the bottom bar to stay visible wouldn't it? We will be annoying some group of users when we align the keyboard behavior, right? Do you think that users who wanted the bottom / top bar sometimes obscured by the keyboard will be greater in number / more annoyed than the users who didn't? My expectation was that more devs would have expected fixed pos elements to stay visible (the Android behavior) and that changing the default fixed behavior to no longer do that would be more annoying.
By changing the ICB behavior on Android we will already annoy authors as 100vh
will behave differently in case the OSK gets shown when compared to the current behavior. Sites that rely on Viewport Units – wether they use position: fixed
or not – will be affected by this. Thanks to this ICB change, though, we can achieve interop between browsers and platforms. This is good for authors, as they can from then on reliably know that their code will behave similarly on all browsers and platforms (both 100vh
and position: fixed
)
By then adjusting how position: fixed
is interpreted – which is proposed in Option 5 (PosFixedViewport) – we would break the just-achieved interop. This seems like a step back, especially since authors can’t easily know wether the browser that’s being used, uses the old or the PosFixed behavior. We would also require all other vendors to implement this change, which might not happen.
Therefore I suggested another route to get to the same result. One where the existing behavior position: fixed
remains in place (which is: lay items out against the LVP), but where we extend position: fixed
with a few new options.
Note that a developer could build the old behavior with
navigator.virtualKeyboard.overlaysContent = true
when iOS adds support for this.
This assumes that browsers adopt both the adjusted position: fixed
behavior and ship the VirtualKeyboard API, and preferably land these at the same time for author convenience.
If the VirtualKeyboard API does not land, then the adjusted position: fixed
would make positioning something against the Layout Viewport bottom impossible (without any hackery)
/* Assumes adjusted behavior from Option 5 */
.fab {
position: fixed;
bottom: 0; /* Above the OSK */
}
.bottombar {
position: fixed;
bottom: ???; /* How can this be positioned against the Layout Viewport? */
}
Looking at https://github.com/WebKit/standards-positions/issues/16 and https://github.com/mozilla/standards-positions/issues/531, there seems to be little excitement for the VirtualKeyboard API.
- Authors cannot easily feature detect wether the new or old behavior is being used. Therefore they cannot predict how the site will look with the OSK shown. This will annoy authors as well.
If we launch this change in coordination with no longer resizing the ICB, there will generally be no visual change for developers. Whereas if we don't, there will be. This does risk annoying developers on iOS / ChromeOS but they would also currently be annoyed by the Android behavior. The
navigator.virtualKeyboard.overlaysContent
allows developers to design their own consistent behavior though it is admittedly not yet supported on iOS.
The “no visual change” you mention would only affect Android. There would still be a difference between Android and non-Android, where we would be dependent on other vendors to achieve interop.
My main concern here was to give authors an easy way to detect wether the current browser has the old or new system implemented. With easy feature detection I mean something like an @supports()
rule. No JS.
By introducing the Fixed Viewport this is not possible. By introducing extensions to position: fixed
it is as easy as this:
@supports(position: fixed / fixed) {
/* Yay, new position mechanism available! */
}
As the alternative proposal aligns all browsers initially, and authors can detect the new behavior, they know for sure where which elements will appear.
.fab {
position: fixed;
bottom: var(--bottom-bar-height); /* Above the bottombar */
}
.bottombar {
height: var(--bottom-bar-height);
position: fixed;
bottom: 0; /* At bottom of LVP */
}
@supports(position: fixed / fixed) {
.fab {
position: fixed / fixed; /* Lay me out against the Fixed Viewport instead of the Layout Viewport */
}
}
In this snippet, the FAB will be obscured by the OSK on platforms that do not support the new mechanism (cfr. what iOS already does). Authors also know it will be above the OSK in case the browser supports the new behavior. It’s about predictability, instead of being reliant on the progress browser vendors make implementing it.
- This does not provide a solution for authors to stick something against the Visual Viewport, something that might come in handy for Floating Action Buttons or Toolbars which do need to stay above the zoomed content.
Presumably this is the device-fixed (option 3) in the above list, right? I.e. not changing with zoom. I do think that this is valuable. My main concern is that device-fixed is not the right behavior for bottom-bars, so even if we add it, it does not solve the bottom-bar issue. I do think
device-fixed
may be independently valuable even though it does have accessibility concerns.
I’m thinking of Option 4 (scaled device-fixed) here. A side-effect of this option is that these elements might obscure content of the page as the user pinch-zooms in. To limit their “zoomability”, the Visual Viewport Units can be used: by applying something like max-height: 20vvh; width: 100vvw;
the element will take up the whole width and at most 20% of the visual viewport height as you pinch-zoom.
I initially considered Option 3 _(device-fixed), but found it a bad idea as it’s hard to grasp the sizing.
100vw
and you then pinch-zoom in. That element will have grown visually. A position: device-fixed
element with the same width set however won’t have grown. Seeing two elements with the same declared width but a different rendered width is pretty confusing.100vvw
a position: device-fixed
element would also be pretty confusing: when pinch-zooming in the Visual Viewport shrinks, so those elements would also shrink as you perform the pinch-zoom.Could we solve this differently? Instead of overloading the position property with new values – such as
device-fixed
,visual-viewport-fixed
, … – could we extend its syntax and provide the viewport as an extra argument? I’m thinking of:
position: fixed / layout
= current behavior, would be the same asposition: fixed
)position: fixed / visual
= fixed against the visual viewport, also when zoomed inTo be clear, is this option 3 or option 4?
Option 4 (scaled device-fixed)
position: fixed / fixed
(* lacking a better name) = the proposed behavior from Option 5 (PosFixedViewport)
- The behavior of
position: fixed
would not be changed, making the adjustments backwards compatibleWe will be changing the position: fixed behavior with the no longer resizing the ICB change, which is not backwards compatible on Android without an update to their behavior.
As mentioned earlier main perspective on this is an interop one, so that we can make things consistent and predicable for authors.
- Authors can feature detect the new behavior and act upon that
You could feature detect this when the keyboard is shown by comparing the height of 100% fixed to the viewport. Alternately the virtualKeyboard API lets developers opt into alternate treatments.
This feels complicated and hacky. A pure CSS solution – using @supports()
– is a much nicer solution.
- Authors can also stick something against the Visual Viewport.
Agreed, this is an independently useful feature - but not the best for bottom-bars.
The use-case I had in mind are interfaces that sport floating toolbars that need to stay in place as the user zooms in on the content. Sizing and positioning those toolbars using the Visual Viewport Units would make them unzoomable (or give them a limited zoom)
The CSS Working Group just discussed viewport positioning vs virtual keyboard
.
There's some useful discussion of use cases in https://github.com/w3c/csswg-drafts/issues/7194 btw.
Had a breakout session with @smfr at the 2023 Cupertino F2F. In summary, two concerns were raised:
Furthermore, the default behavior we have right now should preferably not be changed, and it should be an opt-in indeed.
Furthermore, the default behavior we have right now should preferably not be changed, and it should be an opt-in indeed.
We currently don't have interop though right? I think ideally one of the current behaviors would change.
I like this idea, being able to specify where something is fixed, but I would propose a function syntax, like fixed(visual-viewport)
. This would make it more clear that this is an argument for where you are applying the positioning algorithm. The/
syntax reminds me of positioning that breaks out two directions (i.e. block and inline as in grid). It would also be neat to be able to reference names areas here! I.e. fixed(--area)
Forgive me for jumping in with a comment without having read the full thread above, but @bramus asked me to add a use-case I have for honouring both the visual viewport and layout viewport on a single page.
I’ve pinned a cookie/marketing consent banner to the bottom of the viewport, it’s a non-modal dialog (could probably replace with popover now) that allows the user to interact with the rest of the page without being forced to interact with it (because I’m nice like that).
Then I have other elements that I need to keep above the soft keyboard, for example a ‘view all results’ button for a typeahead search – I want this pinned to the bottom of the visual viewport at all times, whereas the cookie banner can be hidden behind the keyboard as it would get in the way of typeahead (and thus defeat my objective to keep it unobtrusive).
This currently seems impossible with Chrome’s meta tag implementation as you can only choose one behaviour at a document-level. My suggestion on Mastodon was that we could do this if there were a ‘vvh’ CSS unit (‘visual viewport height’), then it would be up to the CSS author to decide where to apply this unit to make adjustments, rather than something layout-based living outside in HTML (meta) or JS events.
I’m currently relying on the visual viewport resize event which is less than ideal for a whole bunch of reasons I’m sure everyone here is aware of.
While Visual Viewport units have already been rejected – see https://github.com/w3c/csswg-drafts/issues/7194 – the reinterpretation/extension to position fixed from this issue could help out here.
You could bottom anchor the one thing the layout viewport and the other to the (unzoomed) visual viewport.
@bramus position: fixed / [fixed/visual]
seems sensible, but to avoid dependence on knowing the height of elements in my example, it would be useful to extend this option by also introducing position: sticky / [fixed/visual]
, otherwise we'd be forever needing to bottom-pad the container with the height of the floating element – which is doable but far less maintainable/responsive.
Having now read more of the above thread, has a ‘zoomed’ media query been suggested/considered?
If there are concerns with an FAB dominating the viewport when zoomed or performance being impacted when evaluating viewport units during zoom, maybe a boolean-, threshold- or step-based media query could be a compromise?
e.g. @media (1 < zoom-level < 2)
(greater than 100%, less than 200%), akin to the level 4 width queries
@media (zoom-threshold: 2)
– more basic threshold
@media (zoomed: true)
- most basic boolean.
(Note: I’m suggesting only one of these would be implemented, they just have varying degrees of complexity)
This would present a multitude of options to authors, e.g. with any of the above, we could opt to only pin while the viewport remains unzoomed:
@supports (position: fixed / fixed) {
@media not (zoomed: true) {
.fab { position: fixed / fixed }
}
}
With zoom-level
, we could adjust sizes for different zoom levels:
@supports (position: fixed / fixed) {
.fab { position: fixed / fixed }
@media (zoom-level > 1) {
.fab { font-size: 0.8em }
}
@media (zoom-level > 2) {
.fab { font-size: 0.6em }
}
}
It's pretty crazy that in 2024 we still cannot show an always visible floating action button at the bottom of the screen with CSS, regardless of screen keyboard being present or not. That's a very needed feature.
Position fixed is positioned and sized relative to a fixed position containing block which in the absence of no established container is the viewport.
The behavior today when a virtual keyboard is shown varies (see demo for interactive examples). In each screenshot, the left image is what the user would see, and the right is a visualization of the viewports with the blue outline being the size of the ICB / layout viewport / 100vh and the red outline being the visual viewport:
iOS (and ChromeOS) shrink the visual viewport but keep the fixed position viewport the same size. As a result, elements which are fixed position to the top/bottom may be scrolled out of view (even while zoomed out). However, showing the keyboard does not affect layout in any way.
Android resizes the page to the height remaining after the keyboard is shown, changing the fixed position viewport as well as the ICB. This has the consequence of doing a relayout and potentially triggering different media query breakpoints, but also means that position: fixed elements are still fully visible while zoomed out.
We would like to unify the behaviors, and agree that not having a global resize is better, however one of the stumbling blocks when we tried to adopt the iOS model is that developers struggle with being able to create bottom fixed position elements, which has lead to various alternate proposals on top of the iOS model. Let's explore some:
device-fixed is a proposed position to position elements to the fixed viewport regardless of zoom. This fixes the issue when the keyboard is shown as the element is shifted up, however when zooming
position: device-fixed
content remains the same size which raises concerns for accessibility as well as not preserving the relative size of content on the page.Scaled device-fixed is an alternative which preserves the zoom scale but still treats the visual viewport rect as the fixed position rect. This allows you to zoom in on
position: device-fixed
content but this results in the content intruding more into the viewport. Also, this mode would likely lead to a relayout during pinch zoom as fixed content needs to change size. None of the other approaches require layout during zoom operations.I propose excluding the keyboard from the position fixed viewport. This leads to a very similar behavior to the android one in that
position: fixed
content avoids the keyboard, except that it would not change the height of the ICB or viewport units. The only content which needs to relayout is that which is sized with respect to the fixed position viewport, and content which is not sized with respect to the viewport can be shifted on the compositor.When you zoom in the same thing would happen as currently happens on android, the visual viewport would zoom in but leave the fixed position viewport size unchanged.
Another advantage of this option is that we could easily use the same adjusted fixed viewport as the scrollport for sticky position elements on the root, whereas options 3 and 4 would not implicitly add any support for sticky without adding e.g. device-sticky.
TLDR; In order to close the interop gap while still allowing developers to position fixed position content above the keyboard I propose we adopt option 5.
Each option links to a demo of that option which can be played with on a desktop browser.