Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[css-contain-2] do we need size containment in a single dimension to enable container queries? #1031

Closed
dbaron opened this issue Feb 14, 2017 · 35 comments

Comments

@dbaron
Copy link
Member

dbaron commented Feb 14, 2017

One thing that came up at the extensible Web summit in Boston on Friday was a discussion of container queries.

While I'd previously suggested tying container queries to CSS containment, one thing I realized is that there will be cases where developers want to fix the width from the outside but still allow an auto height, and then do media queries on the container's width.

I haven't thought this through very much, but it seems to me that this may require a concept of layout containment in a single dimension to be exposed from containment.

@dbaron dbaron changed the title [css-contain-1] do we need layout containment in a single dimension to enable container queries [css-contain-1] do we need layout containment in a single dimension to enable container queries? Feb 14, 2017
@frivoal
Copy link
Collaborator

frivoal commented Feb 14, 2017

Specifically, what would be needed is 1d size containment.
https://drafts.csswg.org/css-containment/#containment-size

@dbaron
Copy link
Member Author

dbaron commented Feb 14, 2017

I think container queries require both size containment and layout containment.

@frivoal
Copy link
Collaborator

frivoal commented Feb 15, 2017

I agree. What I meant is that you should be able to apply regular layout containment (which we already have) together with 1d size containment (which we don't) to get the effect you described.

So authors need both, but the WG only needs to add 1d size containment, since we already have the rest.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed , and agreed to the following resolutions:

RESOLVED: Level 2
The full IRC log of that discussion
<fantasai> Topic:
<Florian> github topic: https://github.com/w3c/csswg-drafts/issues/1031
<fantasai> github topic: https://github.com/w3c/csswg-drafts/issues/1031
<fantasai> Florian: Next one from dbaron...
<fantasai> dbaron: This one is a big issue
<fantasai> dbaron: One of the things that a bunch of web devs really want is what they call "container queries"
<fantasai> dbaron: which basically addresses the sue case of teams that develop widgets or modules that are part of a bigger page
<fantasai> dbaron: Their developing some markup and script and whatever that gets included within a bigger page, and it will have some size
<fantasai> dbaron: The bigger page might use meida queries to e.g. switch from 3 columns to 2 colums, and widgte gets bigger though viewport got smaller
<fantasai> dbaron: If you're implementing the widget, you want to respond to the size that the widget is, not the size of the viewport
<fantasai> dbaron: Bunch of ppl want container queries that actually work, rather than do what ppl do right now which is do layout, flush, and set styles based on that
<fantasai> dbaron: Seems to me it should have some relation to containment
<fantasai> dbaron: that is, ability to do container queries should depend on some kind of ocntainment so that your insides dont depend on your outsides which depend on your insidees
<fantasai> dbaron: The next point is that sometime sppl want to do container query on their width, but have an autho height
<fantasai> dbaron: So, was thinking we want to have layout containment in only one dimension
<fantasai> dbaron: Beyond that haven't thought about it, so this is a "please design me a feature" issue
<fantasai> dbaron: This seems like a relatively high priority feature because ppl do this a lot, and do it by doing flush-restyle loops
<fantasai> Florian: If theyr'e willing to go that far, using 2D size containment is good, set it and then reset your height after you do layout
<fantasai> dbaron: Get ppl doing it for multiple parts of the page, so cycles multiple times
<fantasai> fantasai: why not just have -x -y keywords?
<fantasai> TabAtkins: Hard to define what htat means
<fantasai> eae: Maybe sets wrong expectations, that you wouldn't get same perf benefits
<fantasai> TabAtkins: True, but you do get the benefit that when your'e using resize observer you get predictable behavior and not loops
<fantasai> Florian: Seems like level 2, esp we don't have proposal yet
<fantasai> TabAtkins: This plus resize observer plus custom at-rules, I'm hoping will allow solving this use case
<fantasai> TabAtkins: Been my plan for like 10 years
<fantasai> s/10/2/
<fantasai> RESOLVED: Level 2

@ausi
Copy link

ausi commented Apr 19, 2017

I would really like this feature to help container query scripts to solve the recursion issue. But is size/layout containment in one dimension even possible?

Take, for example, the following structure (assuming that size-x would be the value for containment across the X axis):

<style>
	.wrapper { height: 100px; overflow-y: auto }
	.component { contain: size-x }
</style>
<div class="wrapper">
	<div class="component">
		Content that grows in height via a container query script
		when the width gets wider.
	</div>
</div>

In an edge case this could still lead to an endless loop I think: The width of .component depends on the content-with of .wrapper and the content-width of .wrapper depends on the height of .component (because of the scrollbar). This means that the width of .component would still depend on its contents even though contain: size-x is set.

@gregwhitworth
Copy link
Contributor

@ausi containment only works on the box that it is applied to; so, in your scenario, you're only containing size-x for .component. Thus whatever size-x is defined to do will occur for its content and will then propagate the resulting geometry to .wrapper. Regarding scrolling, it this geometry results in the height being over 100px then you re-layout .wrapper with the scrollbar in place (this is how scrollbars work today)

@ausi
Copy link

ausi commented Apr 6, 2018

Regarding scrolling, if this geometry results in the height being over 100px then you re-layout .wrapper with the scrollbar in place

But wouldn’t this re-layout update the width of .component?

@gregwhitworth
Copy link
Contributor

@ausi Yes - but that is how it works today because in scrollbar auto we don't know if we should show one or not, so if you need one we have to do a second pass. Now, based on the containment set you may NOT get to 100px so you won't have that relayout, but again this isn't new.

@ausi
Copy link

ausi commented Apr 6, 2018

The relayout would change the width of .component, so ultimately the width of .component depends on it’s contents even though contain: size-x is set.

As far as I understand it, this means that implementing contain: size-x in a browser is impossible, because it cannot be assured that the size in one dimension doesn’t respond to changes to the size in the other dimension.

@gregwhitworth
Copy link
Contributor

@ausi I mean it's a valid wrinkle, but personally, I think authors would be ok with this type of "two pass" layout as they normally would want the scrollbar to appear, and probably aren't even noticing (in most cases) that two passes are required. As a result, you can still honor the constraint but only once you've answered the question of "does it have scrollbars?" or not. @frivoal @dbaron thoughts?

@ausi
Copy link

ausi commented Apr 6, 2018

I think the “two pass” layout for scrollbars is basically OK for authors. The problem arises if a container query script comes into play.

The spec for contain: size says:

Its primary benefit on its own is that tools which want to lay out the containing element’s contents based on the containing element’s size (such as a JS library implementing the "container query" concept) can do so without fear of "infinite loops", …

The same “no infinite loops” benefit would be required for contain: size-x, but this is impossible I think. And without this benefit the feature would be useless.

@frivoal
Copy link
Collaborator

frivoal commented Apr 7, 2018

I wonder if this can be solved by making size-x work only when there's a specified width that does not depend on the parent (i.e. no auto, no available, no fit-content, no percentage). This certainly limits the situations in which you can use size-x, but if these are cases which would not work anyway, I think that leave us with cases where it does. Not sure it is still sufficiently useful though.

Alternatively, since I believe the problem is exclusively caused by an ancestor having auto scrollbars, maybe that's what we fix: if an overflow:auto element has a descendant with 1d size containment (2d also? not sure), then the scrollbar must be always visible, regardless of whether there is actual overflow or not (with an allowance for overlay scrollbars).

@ausi
Copy link

ausi commented Apr 7, 2018

I wonder if this can be solved by making size-x work only when there's a specified width that does not depend on the parent (i.e. no auto, no available, no fit-content, no percentage). … Not sure it is still sufficiently useful though.

This would work I think, but it would not be useful for container query scripts anymore.

I believe the problem is exclusively caused by an ancestor having auto scrollbars

Unfortunately, I don’t think scrollbars are the only case this could happen. Take for example this code of a contain: size-y example (CodePen):

<div class="parent">
  <div class="inner">
    <div class="child"></div>
  </div>
</div>
<style>
  * { box-sizing: border-box } /*edit: this line was originally omitted, and was added back in later */
  .parent {
    float: left; /* this makes the width depend on its contents */
    height: 100px;
  }
  .inner {
    padding-top: 10%; /* this percentage is relative to the width */
    height: 100%;
  }
  .child {
    height: 100%;
    contain: size-y; /* would not work in this case */
  }
</style>

(2d also? not sure)

Two-dimensional contain: size is not affected because its specified behavior is “When laying out the containing element, it must be treated as having no contents.”

@frivoal
Copy link
Collaborator

frivoal commented Apr 9, 2018 via email

@ausi
Copy link

ausi commented Apr 9, 2018

However, if you add box-sizing: border-box on .inner, then there is an effect on .inner's content height, and we do have a problem. Damn.

Yeah, I forgot to set the box-sizing in my example (it was only in the linked CodePen).

Unlike scrollbars, I don't see any simple way to fix that one.

Neither do I. I would really love this feature, but the more I think of it, the more impossible it seems to me.

@dbaron dbaron changed the title [css-contain-1] do we need layout containment in a single dimension to enable container queries? [css-contain-1] do we need size containment in a single dimension to enable container queries? Feb 4, 2020
@frivoal
Copy link
Collaborator

frivoal commented Oct 7, 2020

@ausi Thinking again about your example, I think it might not be as much of a problem as I initially though: technically, it's still an issue, but:

  • this uses 1D block-direction size containment, but the thing that authors are mostly calling for for container queries is 1D inline-direction size containment
  • This example relies on how block direction percentage padding resolve against the inline dimension, but the opposite isn't true, so I don't think this example has an equivalent with axes flipped.

So, maybe axis agnostic 1D size containment isn't possible, but maybe 1D *inline-direction size containment is?

Well, we still have the problem of scrollbars, but that is a more tractable one. Or are there more situations where this breaks?

@ausi
Copy link

ausi commented Nov 6, 2020

So, maybe axis agnostic 1D size containment isn't possible, but maybe 1D *inline-direction size containment is?

That would be great news.

Well, we still have the problem of scrollbars, but that is a more tractable one. Or are there more situations where this breaks?

I don’t know, margin/padding came into my mind when thinking about “what css size in one axis gets influenced by the other axis”. I can’t think of another one just now.

@ausi
Copy link

ausi commented Nov 6, 2020

I can’t think of another one just now.

But with the property writing-mode you can flip how margin/padding works and can create elements whose width depends on the height of their content: https://codepen.io/ausi/pen/MWeqwNV?editors=1100

Maybe it would be possible to “disable” such features if single-axis containment is used?

@mirisuzanne
Copy link
Contributor

I've been working on this with @andruud, and we have a few ideas for how we might work around the issues. I don't think these two solutions would necessarily be exclusive, but the first certainly feels "simpler" -- and might help us avoid solving the second in more detail.

The "pinky promise" (no containment)

Our favorite option is to avoid the need for size containment altogether, if we can. What if:

  • We evaluate the CQ against the size the container would have if contain:size was specified.
    • A CQ then implies a promise of containment from the author, at least for the axes present in the CQ expression.
    • Layout proceeds without size containment. The actual result of the layout has no effect on CQ evaluation.
    • In other words, if the author breaks their promise, the CQ will still evaluate as if they hadn't. (The CQs won't be evaluated again).
  • For the problematic "ancestral scrollbar" case, we'll automatically evaluate the CQ twice (since we're doing layout twice), so the second pass would have the CQ evaluate against the scrollbar-aware size.
    • However, if that second pass changes CQ evaluation such that the scrollbar wouldn’t be needed after all (e.g. by setting things to display:none when the container is below a certain width), then the scrollbar remains.

There is some danger that this makes it too easy for authors to stumble into "promise-breaking" behavior, where the container query reports a size significantly different from the final layout dimensions. But the advantages might be enough to offset that concern. It might be worth testing in a prototype.

Making 1D containment work

If 1D containment is needed, it looks to me like that would require making some hard decisions about how the cases above fail consistently while maintaining containment. This isn't ideal, but may also be a worthwhile tradeoff for authors. It would take some more discussion, but something like:

  1. For the sake of determining auto scrollbars on ancestors, the container contributes an infinite cross-axis size (always trigger the scrollbar). This is probably the more common edge-case, but I also think auto-scrollbars might often imply an element has containable size on the cross-axis – so authors could avoid this by using 2D size containment in those cases?
  2. For the sake of resolving percentage-padding on the contained axis, always resolve to auto. Maybe there's a better solution here, but I think this is the existing first-pass behavior in many cases where an element has unknown size, so it's a starting-point.

@frivoal
Copy link
Collaborator

frivoal commented Dec 2, 2020

As for "Making 1D containment work", assuming we've identified all the problematic cases, I think you're thinking along the right lines, and that something like that would solve it. But have we indeed identified all the problematic cases?

@mirisuzanne
Copy link
Contributor

I agree there are likely more 1d containment problem-cases. But I think if we want to go that 1d containment rout -- addressing individual issues as they arise -- we can't be completest about it in the abstract. We'll never know if we have the full list until someone tries building it.

@css-meeting-bot
Copy link
Member

The CSS Working Group just discussed [css-contain-1] do we need size containment in a single dimension to enable container queries?.

The full IRC log of that discussion <dael> Topic: [css-contain-1] do we need size containment in a single dimension to enable container queries?
<dael> github: https://github.com//issues/1031
<dael> miriam: The context is in thinking about container queries and starting from dbaron initial proposal a few years back
<dael> miriam: Easy to imagine how it works with full size containment, but doesn't work for most cases. Works in app-like cases but otherwise falls apart.
<dael> miriam: prop is 1d containment so can contain width of element and query against that but allow height to adjust.
<dael> miriam: Several cases where height of children changes width of ancestors. Scrollbars and % in padding
<dael> miriam: A lot of talk about that making it quite difficult
<dael> miriam: anders and I have been pushing on that.
<dael> miriam: Going through the ideas backwards b/c 2nd one is thinking through how to address issues as they arrise. If we want 1d can we always trigger scrollbar on ancestors, resolve % to auto
<dael> miriam: Not full proposals, but want to push conversation forward
<dael> miriam: That's a start on how might address issues as edge cases so 1d containment would work
<dael> miriam: anders proposed the pinky-promise approach. Idea here is what if we don't require containment on container queries and we resolve the query as if we have full size containment but allow you to make changes so you get different final size then reported
<florian> q+
<dael> miriam: trade offs between but not exclusive of each other. A lot more to resolve on both. Anders isn't here but that's basic context
<dael> florian: Author usibility I think pinky-promise works. Cases where children effect cross axis size are rare and easy to avoid.
<Rossen_> ack florian
<dael> florian: Concerned about implications on impl b/c makes layouts effectively stateful. if doing partial layout you have to do a relayout to figure out size if you hadn't instearted the children you did insert. Order becomes meaningful as well which brings us back to having to do a layout of the anti-page
<bkardell_> q+
<dael> florian: Not impossible, but could be expensive. I'm concerned. THe plug the leaks one by one seems more possible. We haven't come up with so many examples. We've so far had 2. Maybe can plus one by one
<dael> florian: Seems that by far dominant of 1d is query inline size, not block. The leaks for inline and block are not same. Easier to plug inline leaks. If we jsut worry about those maybe slightly easier problem
<dael> bkardell_: In the pinky-promise thing doesn't that wind up negative auto-height?
<Rossen_> ack bkardell_
<dael> florian: No, the pinky-promise case you do a container query against width and then you say I won't do anything in layout what would change the width of the parents. As long as you don't have the cases that is does change width of parents everything is fine
<dael> miriam: One other complication is inside of floats that shrink wrap, container query would return 0 but layout is quite a bit different
<dael> florian: Yeah. But for shrink-wrap this is fundamentally problematic.
<miriam> +1
<dael> florian: If there is a use case for that it's a whole different can of worms
<dael> Rossen_: We're at time. not sure if we have a resolution we could call. I would urge you to continue discussing on GH
<iank_> I'm somewhat not convinced the pinky-promise will yeild the results web-developers expect.
<dael> TabAtkins: Not looking for resolution yet, just general information

@astearns astearns removed the Agenda+ label Dec 3, 2020
@andruud
Copy link
Member

andruud commented Dec 4, 2020

The CQs won't be evaluated again.

This poor wording is my fault. If we need to do another layout pass, which can be the case for e.g. the ancestral scrollbar problem, then the CQ would be evaluated again with the scrollbar effects present. However, if the author then conditionally (based on the scrollbar appearance) restyles things such that a scrollbar would ultimately not be necessary after all, then too bad. You get a useless scrollbar. We're saved by the fact that (in Blink at least) we don't re-layout until we get something stable, we layout at most twice (for the scrollbar case anyway).

I don't think it's very practical to maintain an entirely different page-wide reality where some elements are size-contained. I've assumed that we can cheaply and "locally" compute the would-be-contained size for containers whenever they get a new (actual) size. If that's not true, or if we do have "repeat layout until stable" behavior somewhere in a way which can affect the would-be-contained size, then pinky-promise probably doesn't work.

@frivoal
Copy link
Collaborator

frivoal commented Dec 7, 2020

I've assumed that we can cheaply and "locally" compute the would-be-contained size for containers whenever they get a new (actual) size. If that's not true, […] then pinky-promise probably doesn't work.

I believe that's indeed not true, because evaluating them cheaply and locally like that makes layout stateful, which is bad. The current state of the layout should not matter when (re)evaluating the layout. If this is true, when browsers evaluate the layout is an unobservable implementation detail, and they may do it as often or as rarely as they want to, recomputing it for part of the page or for the whole page, and nobody can tell the difference. This is an important quality to maintain, and it rules out the pinky-promise approach (unless we recalculate everything form a clean state every time, but that would be expensive).

@FremyCompany
Copy link
Contributor

Just thinking out there, maybe we can add inline-size containment by giving the available width rather than the actual width of the container. This has the downside that this might mean we need to redo styling multiple times, if the element needs to be layouted under min/max constraints, but maybe this can be done in one go by computing a few styles options (min-width, parent-size, max-width/unconstrained, ...).

This needs some more thought, I just wanted to put this out there, so I remember I had this thouht the next time this is being discussed.

@ausi
Copy link

ausi commented Mar 26, 2021

I just played around with contain: inline-size and @container in Chrome Canary 91.0.4459.0 with the enable-container-queries flag. (And it is awesome 🤩)

But trying my example from #1031 (comment) results in inconsistent layout jumping between two states. See https://codepen.io/ausi/pen/dyNMYeG

@andruud Is it too early to report such bugs on bugs.chromium.org?

@andruud
Copy link
Member

andruud commented Mar 30, 2021

@ausi Yeah, issues like that are expected at this point, but please do report any "interesting" cases you come across nevertheless. 🙂

@lilles
Copy link
Member

lilles commented Apr 9, 2021

@ausi Thanks for the report. I've reported 1197029. The current code that does an extra pass for auto-scrollbars assumes that scrollbars taking up space is the only way for auto-scrollbars flipping back and forth, but that's no longer the case for container queries, so we need to notify the closest scrollable container that container query evaluations changed during a layout.

@lilles
Copy link
Member

lilles commented May 20, 2021

@ausi Thanks for the report. I've reported 1197029. The current code that does an extra pass for auto-scrollbars assumes that scrollbars taking up space is the only way for auto-scrollbars flipping back and forth, but that's no longer the case for container queries, so we need to notify the closest scrollable container that container query evaluations changed during a layout.

Update: the issue has been fixed in our prototype.

@mirisuzanne
Copy link
Contributor

@bfgeek, @fantasai, and I drafted some initial text for defining inline-size containment in 8577aa2 (and removed block-size single-axis containment entirely). I plan to flesh out the example that's there, and add several more examples demonstrating how inline-size containment behaves in the various 'problem cases' that have been raised.

@frivoal
Copy link
Collaborator

frivoal commented Dec 22, 2021

Closing. The titular question has been answered, and the spec has been clarified. The resolution wasn't taken in this issue, but the CSSWG did resolve on this topic.

@frivoal frivoal closed this as completed Dec 22, 2021
lilles pushed a commit to lilles/csswg-drafts that referenced this issue Feb 10, 2022
The definition of container-type:block-size was removed when block-size
containment was removed, but the value syntax still had it.
@bramus
Copy link
Contributor

bramus commented Jun 13, 2022

@mirisuzanne Could it be that the changes from 8577aa2 haven't been merged yet? Current ED allows block-size, but the resolution of this issue and your PR seem to hint otherwise.

@andruud
Copy link
Member

andruud commented Jun 13, 2022

@bramus You can still query block-size on containers with full size containment (container-type:size).

@bramus
Copy link
Contributor

bramus commented Jun 13, 2022

Ah yes, of course 🤦‍♂️

mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
Add support for 'inline-size' and 'block-size' as keywords for the CSS
contain property. There is no specification yet, but there are two
github issues[1][2].

The syntax implemented here does not allow more than one size
containment value per declaration.

[1] w3c/csswg-drafts#1031
[2] w3c/csswg-drafts#5796

Bug: 1146092
Change-Id: I79cf3d4cb22e8e2705d5f2f7348f4513e4eac2d7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2653205
Reviewed-by: Morten Stenshorne <mstensho@chromium.org>
Reviewed-by: Anders Hartvoll Ruud <andruud@chromium.org>
Commit-Queue: Rune Lillesveen <futhark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#848577}
GitOrigin-RevId: 0ecdc725f612e6899bb452b74650101625bb9815
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests