Table of Contents

Layout and Size containment are supported in all major evergreen browsers:

.sidebar {
contain: layout size;

Layout Containment

Isolates the layout-impact of contents in several ways:

This ensures that:

Size Containment

Isolates the size-impact of contents:

This feature primarily exists to make container queries possible. When combined with layout containment, it allows:

Is single-axis containment even possible?

2-axis size containment might work for some app-style layouts, where layout areas are sized to the viewport, and each area is a scroll-container. But most web layout relies on specifying inline-size (often horizontal), and allowing content to expand or contract on the block-axis (often vertical).

We need container queries to support that more common use-case.

Conceptually, single-axis containment should not be hard to implement if we can describe how it works. But that may still be challenging…

Single-axis containment only works if we can ensure that changes in the cross-axis have no impact on the contained axis. There are several places where CSS makes that difficult:


When a parent element has auto scrolling, extra cross-axis size can cause scrollbars to appear on the contained-axis. This is only an issue when all three are true:

This could be solved by forcing in-flow auto scrollbars to be always-visible when there are nested single-axis containers.

Percentage Padding

Block-axis percentage padding & margins are resolved relative to the inline available size. That would cause issues when:

See contain-y comment) and related codepen demo.

That issue is most likely to occur when containing the block-axis, but nested writing modes can flip the impacted axis (contain-x with writing modes).

It’s interesting that browsers are already inconsistent about rendering that last demo. They also disagree on simpler percentage calculations when “in a particular axis, the containing block’s size depends on the box’s size” [Box Sizing 3].

The sizing/layout specs all have module-specific caveats for handling those cases. This seems to me like it could be solved with one more caveat? Not solved as in perfect layout, but at least consistent & implementable, without infinite loops.

I have trouble imagining actual use-cases that would require this to behave well.

✅ Option: Containment with edge-cases

If 1D containment is needed, we would need to make 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.

This may not be a complete list of issues/solutions, but the point is that it shouldn’t block an attempt at moving forward. A prototype would help us expose/address additional issues.

❌ Option: No containment (“pinky promise”)

According to browser engineers, this approach won’t be possible.

Anders proposed this as a solution. I don’t know how feasible it is to implement, but I like it in theory. What if:

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.

Would it make layout “stateful”?

See Florian’s comment on the CSSWG thread

How would this interact with floats?

Eric Portis asked:

Are queries in the block direction, or on floated elements, all going to evaluate against 0, then? I do worry this’ll be confusing…

I think the answer is yes, but I’m not sure containment improves this. None of the options fully support a query against elements that take their size from children. The question is how we respond to that case:

In either case, we’ll need to teach the concept that queries rely on external sizing to be accurate.

Rune Lillesveen pushed that a bit further, asking about containers inside floats:

I was thinking about the case where you have an auto-width block inside a float and other content that contributes to min/max widths. What’s the width that a CQ on #auto evaluates against and when does that happen? Will that width possibly change multiple times during layout?:

<!doctype html>
#float { float: left }
#auto { height: 200px; background: blue }
<div id="float">
<div id="auto"></div>

To me this looks like the same basic problem, unless there is something special about the timing issues?

How does it scale when nested?

From Eric again:

How does “pretend it didn’t happen” scale when you have nested containers & queries? Do you pretend for each nested container in turn, (so you have to run layout the-max-nested-depth times, I think?), or do you pretend every queried container has contain set, all the way up the chain, once (so you’re “only” running layout twice, once for real and once for query resolving purposes?)

This is a question for someone with more knowledge of browser rendering & layout internals.

According to Rune:

This sounds like what would have happened if we fully finished layout for each container, re-evaluated container queries, then went back to style recalc, then layout for a container at the next level, etc. instead of calling style recalc from layout and continue in the same layout pass.

I think this reflects some of their thinking about how to “interleave style and layout” – needs follow-up…