w3c / csswg-drafts

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

[css-contain][css-overflow] Setting containment on root should not make scrolling impossible #9003

Open mirisuzanne opened 1 year ago

mirisuzanne commented 1 year ago

Container size queries provide a number of advantages over media (viewport) size queries, besides the ability to query nested elements. In order to access those benefits, and also provide a 'default' outer container, authors will commonly want to add containment to the root element . In addition, authors rarely rely on intrinsic sizing of the root - setting html { height: 100%; } is a well established pattern in reset styles, to size with the viewport – making the root a good candidate for 2-axis size containment.

The resulting expectation is that this should work:

html {
  container-type: size;
  block-size: 100%; /* extrinsic viewport height */
  overflow: auto; /* scrollbars if needed */
}

However, this doesn't currently work as expected in any browser. See this simplified demo on codepen. The primary issue seems to be a combination of factors:

  1. size containment (and the explicit height or block-size properties) keeps the root element from resizing to it's contents
  2. The root element never shows scrollbars, they are always propagated to the viewport
  3. In many cases, the body element overflow value is used instead of the root element overflow
  4. layout containment stops propagation of the overflow property from the body to the viewport

This is not supported by CSS Overflow and CSS Containment specs:

In my mind that means:

In another conversation, @fantasai has also suggested that we might want to explicitly propagate root containment to the viewport.


Note that this also causes issues if we change the containment to inline-size, because it generates a new positioning context on root. Since overflow still propagates, and root isn't able to be a scroller itself, position: fixed elements no longer remain in view when scrolling. I have another slightly more complicated pen that helps explore all the various interactions. In order to achieve roughly the desired outcome with either 1d or 2d containment, authors would currently have to set:

html {
  container-type: size; /* or inline-size */
}

html, body {
  block-size: 100%;
}

body {
  overflow: auto;
}

This allows the body to act as the outer scroller, with fixed elements still positioned by the root.

Ideally, our solution here should allow both size and inline-size root containers, without breaking viewport overflow or fixed positioning.

lilles commented 1 year ago
  • When we have a non-default root overflow specified, it should absolutely propagate to the viewport. This is certainly a browser bug (in all browsers).

I'm pretty sure the Chrome implementation propagates the overflow value to the viewport, but that what you see is instead an effect of how overflow is treated for contain:layout.

<!DOCTYPE html>
<style>
  html {
    contain: layout; /* No scrollbars, change to size and you will have scrollbars. */
    overflow: auto;
    height: 0px;
  }
  #filler { height: 10000px; }
</style>
<div id="filler"></div>
mirisuzanne commented 1 year ago

@lilles I see. So overflow does propagate to viewport, but containment remains on the root which captures any nested overflow?

In that case, I think @fantasai's proposal that 'containment should also propagate to the viewport' would be the best solution – since containment and overflow would be defined on the same 'element'?

Loirooriol commented 1 year ago

Would the effects of containment be well defined on the viewport? Another possibility seems to not propagate overflow from the root if it has containment. We are already preventing propagation from the body anyway.

css-meeting-bot commented 1 year ago

The CSS Working Group just discussed [css-contain][css-overflow] Setting containment on root should not make scrolling impossible.

The full IRC log of that discussion <fantasai> miriam: Decided with container queries not to have container on root
<fantasai> miriam: but said authors should be able to set root as container, and be able to query it similar to media queries with some advantages of it being an element
<fantasai> miriam: but it doesn't quite work
<fantasai> miriam: because if you set size/layout containment on root element
<florian> q+
<fantasai> miriam: the overflow property propagates to the viewport, but size/layout containment causes the root to not have any scrollable overflow
<fantasai> miriam: so we need to either say, when you set containment on root, the overflow property doesn't propagate to root and get scrollbars
<fantasai> miriam: or we propagate containment to the viewport
<fantasai> miriam: not sure if that means anything other than "it doesn't apply to the root"
<Rossen_> ack florian
<fantasai> florian: I believe the first one has a well-defined behavior that's useful
<fantasai> florian: One thing that makes it ok is that when we no longer propagate to viewport, the the viewport gets default value of 'overflow' which resolves to auto on the viewport
<emilio> q+
<fantasai> florian: therefore if the root itself would overflow the viewport, you get scrollbars to see all of it. It might be odd, but you can access all the content
<emilio> fantasai: I think that having the containment and overflow apply to the root element would be pretty terrible
<emilio> ... because if you do size/layout containment you get 0-height and no overflow
<emilio> ... if you also have paint containment you also clip
<emilio> ... if you compensate by making the root element larger
<emilio> ... then you have a scrollbar which is inside the border edge of the root element rather than to the viewport
<florian> q+
<emilio> ... which is also not great
<Rossen_> ack florian
<florian> q-
<Rossen_> ack fantasai
<emilio> fantasai: I think the first option is terrible and we should propagate containment to the viewport
<fantasai> miriam: you would likely uses 100% rather than 100 vh
<fantasai> miriam: but it's only minimally better
<fantasai> miriam: I agree that the other is nicer for authors
<fantasai> Rossen_: would that be compatible? then percentages on body would resolve (whcih wouldn't previously)
<Rossen_> ack emilio
<florian> q+
<dbaron> emilio: What's the difference between propagating containment to the viewport rather than just not applying containment to the root?
<dbaron> Scribe+
<fantasai> iank_: containment on root is probably pretty rare in the web
<dbaron> miriam: It establishes a container for querying.
<fantasai> iank_: remind me the syntax?
<fantasai> miriam: @container [...]
<fantasai> emilio: so the size of the root...
<dbaron> (I think what I scribed above goes here)
<fantasai> emilio: what you want isn't the root element box size, it's the viewport?
<fantasai> miriam: I was assuming you'd get the root size, but in general what the author would do is size the root to 100% so they can get the viewport size
<fantasai> miriam: the difference between that and media query is that you can use actual font sizes (for font units)
<fantasai> miriam: other than that you want a container that's a fallback for everything for the page
<fantasai> emilio: isn't the fallback already the root?
<fantasai> miriam: no, there is no default
<Rossen_> q?
<fantasai> miriam: we intentionally didn't, because there are cases where it would be surprising if you didn't expect it
<fantasai> florian: so, size containment doesn't mean size to zero. It means size as empty
<fantasai> florian: most typically case is querying width, so empty isn't zero
<fantasai> florian: it would work better than viewport, because if there are margins etc, then you are considering those
<fantasai> florian: in the block axis, yes, you would size the root to zero
<fantasai> florian: but is it that bad?
<emilio> q+
<fantasai> florian: I'm not sure the scenario is that bad as imagined
<Rossen_> ack florian
<fantasai> emilio: fallback is for container units but not container queries?
<Rossen_> ack emilio
<fantasai> emilio: that's a bit weird
<fantasai> miriam: yes
<fantasai> emilio: ignoring containment on root to make this work seems sketchy
<fantasai> emilio: I am a bit torn
<fantasai> dbaron: I'm struggling at this point to understand the ways in which ignoring containment on the root and propoagating to the viewport different
<fantasai> dbaron: (other than for container establishing effects)
<fantasai> dbaron: though I guess they differ because of the size of the root, but unsure what that means for effect on developers
<florian> q?
<florian> q+ fantasai
<fantasai> emilio: is alternative to this to provide an opt-in to fall back to viewport somehow?
<fantasai> emilio: if we didn't do the fallback because we think it's confusing.. then what makes it not confusing would be having an opt in
<fantasai> iank_: it's confusing if you're writing these rules and you don't expect it to resolve yet because you haven't specified a container, and then it suddenly resolves
<fantasai> miriam: if you're mainly using CQ on small things, and end up not being in a small thing and get a very large result
<fantasai> miriam: can be confusing
<fantasai> miriam: bu in other cases want the nearest container, and in that case want the fallback
<fantasai> miriam: if you added containment on root, you can add a name so that you can reference it
<fantasai> miriam: if you're in control you can handle both use cases, if not can only handle one
<Rossen_> ack fantasai
<emilio> fantasai: don't know what to say
<emilio> florian: put you on the queue about the scenario you were concerned about
<emilio> ... not sure what happens
<emilio> fantasai: containment works in both axes right?
<emilio> florian: not necessarily, if you only do it on one axis doesn't collapse to zero
<emilio> ... in the block direction if you set it to a 100% shouldn't it be fine
<emilio> ... if we propagated neither overflow nor containment, shouldn't it work?
<emilio> ... you'd set the block size to 100%
<emilio> fantasai: if the content needs to scroll you wouldn't be using the viewport scroller but an internal scroller
<emilio> ... is it the same?
<emilio> q+
<fantasai> emilio: It's not the same. There are UA features that only work on the root scroller, like marking find-inpage things, etc.
<fantasai> smfr: anothre difference is bakground-attachment fixed is optimized for root scrolling
<fantasai> Rossen_: let's take the issue back to GH and talk about it there, once we have a path for resolution we can bring it back
<fantasai> me github-bot, topic https://github.com/w3c/csswg-drafts/issues/3473
mirisuzanne commented 1 year ago

This issue is primarily about establishing a root container for the sake of container queries. One possible approach is to change the way container properties are applied to root, rather than changing how the contain property applies. The container property often applies containment, but not directly - it represents the authors desire for a queryable container.

For style queries, there is no need to establish a container, but it is possible to name containers. Authors establishing a container name on root will expect that their style queries are resolved on the root element. It would not make sense to ignore styles set on the root element, and it's not even clear to me what it would mean to query styles on viewport. So the issue here is also specific to size queries, and any solution we come to should keep other query types (and container names) intact.

With size queries, there are several things that could differentiate a root element container from a viewport container:

  1. The viewport size is entirely determined by the client, while the root element size can be changed by author styles (but not contents, since it would be a container)
  2. Viewport scrolling is optimized, and has additional functionality, which a scrollable root element can't provide. A root element container would also need to contain scrolling
  3. Relative units and custom properties in queries are resolved on the container. the viewport and root element would resolve those values differently in most cases

Point 3 is the primary advantage that authors might get from using size container queries on root, instead of using a media query. The other main advantage of establishing a root size container is simply to have a fallback for container queries that don't find any intervening containers.

In my experience, it is very common for authors to set a viewport-based height on the root element (eg 100vh or 100%), and very uncommon for authors to set a margin on root. Because of that, the root container is generally either content-sized (which would not be possible when it's a container) or it is viewport-sized. When establishing a root container, authors are even more likely to set the size of the element based on the size of the viewport. Losing the ability to measure the root element is not a big loss in most cases.

Is there any possible solution here that allows scrolling on the viewport, while continuing to support value resolution (and container names) on the root element? Even if it means we always return the viewport size on root containers?

luis-pato commented 1 month ago

Any news here? As an author i find it very strange that making the root element into a container prevents scrolling. I have also noticed that the behavior isn't coherent across the different browsers (for instance Chrome v129 and Firefox v131).

Maybe we shouldn't be turning the root into a container, but in some cases it may actually be useful...

mirisuzanne commented 1 month ago

If there are differences, we should address those. Do you have examples of the differences?

Beyond that, my sense is there might not currently be a path forward on this. As a workaround, I generally apply containment to the main layout blocks directly inside the body – something like this:

body > :is(header, nav, main, aside, footer) { container: layout / inline-size; }

I've written a more in-depth article about this if you're interested.

luis-pato commented 1 month ago

@mirisuzanne I am not really sure what causes the differences, so i can't explain them very well. I can only describe what i saw... On Chrome v129 everything seemed to work fine, but on Chrome v128 we couldn't scroll. Also on Firefox or Safari we couldn't scroll!

Yes, i have seen you article. It was actually what lead me to this gh issue. Thank you, i found your article really helpful! 🤗

I have also tested the solution you have at the end of your article. But i ended update having some problems with fixed elements overlapping the scrollbar, something like this: image

In the end i did actually use something very similar to what you suggest here on your comment. i turned the main, footer, ... into containers.

But, tbh, i think the best would be to just turn the elements themselves into container. The problem with that is that each element needs an extra "container wrapper", so the element can be the container, and the wrapper can listen to the parent @container

mirisuzanne commented 1 month ago

On Chrome v129 everything seemed to work fine, but on Chrome v128 we couldn't scroll. Also on Firefox or Safari we couldn't scroll!

@bfgeek Would the move away from full layout containment be responsible for this difference? If so, would that now allow using the root as a container once all browsers update their implementation?

i think the best would be to just turn the elements themselves into container.

Yeah, that would be great – but raises even more issues, since then we would be styling the same thing we're measuring. See #10509 for more recent discussion on that.