Of the seven effects layout containment applies, container-type: inline-size now keeps exactly one. The CSS Working Group took the other six away in 2024, and the warning you’ve heard a hundred times — that a size container becomes a stacking context and a containing block for fixed-position descendants — stopped being true in Chrome and Firefox that year, and in Safari the following March.
A media query reports the viewport. A container query reports the box a component was put in, and that is the only one of the two a component can know about, because a component does not choose where it is placed. Every other difference follows from that one.
What container queries fix that media queries never could
Container queries fix the guess at the centre of every breakpoint. A media query makes you decide what the page looks like at 768px, when the thing you need to decide is what a card looks like with 320px of inline space, wherever that card sits.
The CSS Conditional Rules Module Level 5 editor’s draft defines the opt-in in section 5.1:
Establishes a query container for container size queries on the container’s own inline axis. Applies style containment and inline-size containment to the principal box, and establishes an independent formatting context.
Three effects, and layout containment is not among them. Style queries need no opt-in; size queries do, and the opt-in is the part that costs you something. Choosing what a component adapts to is the same decision as choosing what a mobile app adapts to, which is why native or cross-platform settles it on hardware.
Media queries ask the viewport a question the component cannot use
A media query can only ask about the output device, and Media Queries Level 3 says so in one sentence, at the top of section 2 in the Recommendation re-issued on 21 May 2024:
A media query consists of a media type and zero or more expressions that check for the conditions of particular media features.
A media type is screen, print or all; a media feature is width, resolution, orientation, hover, prefers-color-scheme. None is a property of an element, and none changes when a card moves from a 900px main column into a 280px sidebar.
The units give the game away. Section 1.3 of Media Queries Level 4, the Candidate Recommendation Draft of 19 February 2026, anchors them to the initial value rather than to anything on the page: “in HTML, the em unit is relative to the initial value of font-size … not any styling on the page”. So (min-width: 40em) is one fixed pixel number for a given reader, whatever you did to typography. Correct for a media query. Useless to a component, and before 2022 CSS offered nothing else.
From fixed widths and m-dot subdomains to @container: how the question moved
The question moved in four steps, and only the last changed what was being asked. Fixed pixel widths came first, and the 960 Grid System still describes itself as “commonly used dimensions, based on a width of 960 pixels”. One canvas, and a horizontal scrollbar for anyone who disagreed. Separate mobile URLs came second, and Google’s mobile-first indexing guidance still documents that pattern as one of three configurations, “also known as m-dot”.
The third step is the one with a name. Ethan Marcotte’s “Responsive Web Design” went up on A List Apart on 25 May 2010 and gets to the point in nine words: “In short, we need to practice responsive web design.” The ingredients come later:
Fluid grids, flexible images, and media queries are the three technical ingredients for responsive web design, but it also requires a different way of thinking.
Read that list with a component in mind. A fluid grid is a property of the parent; so is a flexible image. A media query is a property of the viewport, which belongs to no component at all. Not one of the three belongs to the thing being laid out, which is why a component library built on all three still needs its author to know where each one ends up.
(The dates matter. W3C’s publication history for Media Queries Level 3 runs from a first Working Draft on 4 April 2001 to a first Recommendation on 19 June 2012, then on through two re-issues since: 4,094 days to that first Recommendation, or 4,094 ÷ 365.25 = 11.2 years. Marcotte’s article landed 756 days before it. Two years across the 2012 leap year is 731 days, so 756 − 731 = 25: the technique went into production two years and 25 days before the W3C first called it a Recommendation.)
@container arrived in Chrome 105 on 30 August 2022, the stable_date in Chromium’s milestone record. Firefox 110 followed on 14 February 2023, Safari 16 after that. Marcotte to Chrome is 4,480 days.
Does container-type: inline-size create a stacking context?
No, and it hasn’t since Chrome 129. Firefox 133 and Safari 18.4 followed. The change is recorded where almost nobody reads: the Changes appendix of the CSS Conditional Rules Module Level 5 Working Draft, below the Security and Privacy Considerations sections. Fourteen entries sit under “Changes since the Working Draft of 5 November 2024”, and one of them reads “Dimensional query containers no longer apply layout containment (#10544)”. The resolution came first. The CSSWG agreed it on 24 July 2024, and issue #10544 stayed open until 19 December, by which time two engines had shipped.
What the line removes is bigger than it looks. Section 3.3 of the CSS Containment Module Level 2 editor’s draft defines layout containment as a numbered list of seven effects: an independent formatting context, fragmentation trapping, overflow treated as ink overflow, an absolute- and fixed-positioning containing block, a stacking context, forced breaks that don’t propagate, and no baseline. container-type keeps the first and drops the other six. The two that broke real pages are items four and five.
WebKit says it in one line, under Resolved Issues in “WebKit Features in Safari 18.4” on 31 March 2025: “Fixed: Dropped layout containment from container-type. (132549134)”. mdn/browser-compat-data carries it as a sub-feature, weak_containment, “Applies style and size containment, but no layout containment”. Chrome 129 went stable on 17 September 2024 and Safari 18.4 on 31 March 2025: 195 days from first engine to last. Firefox 133 landed 70 days after Chrome, on 26 November 2024, and Safari 125 days after Firefox. 70 + 125 = 195, the same window by a different route.
The change did not go unnoticed. Chris Coyier chased what looked like a z-index bug in Safari and wrote it up on 3 January 2025 as “Containers & Context”; what he was looking at was item five, still applied by the one engine that hadn’t changed. Nobody counted the list.
I am less sure about the Chrome end than I’d like. Chrome 129’s release material doesn’t mention the change, and Firefox 133’s release notes don’t mention container queries at all. The direction is not in doubt: WebKit says so and the CSSWG recorded it. The exact milestone might be off by one, and I’d check a Chromium commit before betting on it.
The documentation hasn’t caught up, and you can confirm that in under a minute. On 29 October 2026, MDN’s container-type reference page gave the inline-size value as “Applies style and inline-size containment to the element.” MDN’s own Container queries guide gave it as “Applies layout, style, and inline-size containment to the element.” Same site, same day, opposite answers.
And the specification contradicts itself. Section 6.1 still says a size feature evaluates to unknown unless the principal box is “a layout containment box”, a condition section 5.1 no longer creates. The behaviour is settled in three engines. The prose defining it is two years behind.
Use container-type on an ancestor, never on the element you are querying
Put container-type on an ancestor of the element you want to restyle: the specification looks upward. Section 5.4 of CSS Conditional Rules Level 5 says “the query container to be queried is selected from among the element’s ancestor query containers…”, so an element cannot query its own size. When nothing above it qualifies you get no error and no warning, because “If no ancestor is an eligible query container, then the container query is unknown for that element” — the block is skipped and the component keeps its fallback styles.
css
/* the wrapper opts in; the component asks */
.card-slot {
container-type: inline-size;
container-name: card;
}
.card {
display: grid;
grid-template-columns: 1fr;
}
@container card (inline-size > 30em) {
.card {
grid-template-columns: 8rem 1fr;
}
} Move container-type onto .card and the query just stops matching, silently.
The length units fail worse. Section 7 of the same document says that for cqw, cqi and the rest, “if no eligible query container is available, then use the small viewport size for that axis”. So 1cqi becomes 1% of the small viewport, and the component renders at a plausible wrong size rather than an obviously broken one.
One thing weak containment did not fix is why components collapse. Section 3.2 of the CSS Containment Module Level 2 editor’s draft defines it:
Giving an element inline-size containment applies size containment to the inline-axis sizing of its principal box. This means the inline-axis intrinsic sizes of the principal box are determined as if the element had no content.
Anything sized by shrink-to-fit loses its width the moment you set container-type: inline-size on it: a float, an inline-block, a grid item at width: max-content.
container-type: size is the value I would not ship at all. MDN’s container-type page gives the consequence plainly: “If a contextual or explicit size is not available, elements with size containment will collapse.” You assert a block size you don’t know, to query a height you rarely need. inline-size is the only value we put into production on software development work.
Why 40em is 640px in one container and 960px in another
Because em inside a container query resolves against the query container’s computed font-size, not the root and not the reader’s default. Section 6.1 of CSS Conditional Rules Level 5 says “relative length units … are evaluated based on the computed values of the query container”, then adds the note that matters: “Note: This is different from the handling of relative units in media queries.”
The specification’s own example does the arithmetic. An aside at font-size: 16px and a main at font-size: 24px, both with container-type: inline-size, and one rule across both: @container (width > 40em). Inside the aside, 40 × 16 = 640, so the rule is true above 640px. Inside the main, 40 × 24 = 960, true above 960px. The gap is 960 − 640 = 320px, and 320 ÷ 640 = 0.5, so one stylesheet line moves its own breakpoint by 50%.
A breakpoint looks like the number you typed. It isn’t. The hidden factor is a font-size inherited from a file you aren’t actually looking at.
What @container still cannot do in 2026
Four things, and the first is the one people trip over. An element cannot query itself: section 5.4 of CSS Conditional Rules Level 5 selects the container from the element’s ancestors, so a component that responds to its own slot needs a wrapper that exists only to be measured. Second, the dimensional feature list is closed at six: width, height, inline-size, block-size, aspect-ratio and orientation, across sections 6.1.1 to 6.1.6. Nothing asks how much content a container holds, or how much room is left in a grid track.
Third, responsive images sit outside the system. The HTML Standard gives the sizes grammar in section 4.8.4.2.2 as <source-size> = <media-condition> <source-size-value> | auto: a media condition, with no container condition permitted. A layout could adapt to its box while the image inside still picks a source from the viewport.
Fourth is a judgement rather than a limit. container-type: scroll-state is recorded in browser-compat-data at Chrome 133, with Firefox and Safari both false, which makes it a one-engine feature with the silent failure mode above. I wouldn’t put a scroll-state() query into production until a second engine records support, and the wait might be long: by css/at-rules/container.json, style queries for custom properties took 559 days to get from Chrome 111 on 7 March 2023 to a second engine, Safari 18 on 16 September 2024. Firefox 151 took another 610, to 19 May 2026. 559 + 610 = 1,169 days to all three.
When clamp() is the better answer than a container query
Use clamp() when the widest and narrowest slot a component sits in differ by less than roughly 1.5x. Above that ratio a component genuinely has two layouts and a container query is the honest way to say so. Below it you are scaling type and padding, and clamp(), which MDN’s clamp() page defines as “max(MIN, min(VAL, MAX))”, does that with no containment and no ancestor to maintain. Three engines took clamp(): Chrome 79, Firefox 75, Safari 13.1. Its Baseline line reads “It’s been available across browsers since July 2020”, over two years ahead of @container.
The 1.5 is chosen, not measured. The tolerance I want: a component pays for containment only when one stylesheet line would otherwise produce two visibly different layouts. The distribution I have is the specification’s own worked example: 640px and 960px. 960 ÷ 640 = 1.5. The same number arrives independently from the font sizes, because the 40em cancels: 24 ÷ 16 = 1.5. Two routes, one number, and a CSSWG editor picked the inputs, not me.
Suppose the 1.5 is wrong. If the right threshold were 3.0 — a 2x derating — a component in a 320px slot and a 640px slot flips from container query to clamp(), and I would have been wrong about it. Derate to 0.75 and every component with two slots qualifies, which is worse. What survives both is the lower bound: a component in exactly one slot earns nothing from container-type at any threshold and pays an independent formatting context for it. Double the slot count and the rule gets easier. Four slots at 320, 480, 640, 960 put the widest over the narrowest at 960 ÷ 320 = 3.0. Halve the spread to 480 and 640 and 640 ÷ 480 = 1.33, under the line.
And the gap this piece opened with has not closed. Marcotte put responsive design into production 756 days before the W3C first called Media Queries Level 3 a Recommendation, and Chrome shipped @container on 30 August 2022, twelve days after the Working Draft of 18 August 2022 that is still W3C’s latest published copy. Its §4.1 still says inline-size applies layout containment, and an example further down the same section prints container-type: style;, a value the property table above it never allowed: normal || [ size | inline-size ]. So read drafts.csswg.org, not /TR/. That argument is older than container queries, and /TR/ really is the stable record. It is the wrong one for a property shipping in three engines, where the editor’s draft matches the browser in front of you.