Design · · 6 min read
Container style queries are Baseline. Here's what actually shipped
Style queries became Baseline newly available in May 2026, but only for custom properties. The syntax, the ancestor rule, and the four things that catch people out.
A container style query applies styles based on the computed value of a custom property on an ancestor:
.sidebar { --density: compact; }
@container style(--density: compact) {
.card { padding: 0.5rem; gap: 0.25rem; }
}Any .card inside .sidebar picks up the compact padding. The card doesn't need a class, a data attribute or knowledge of where it sits — the context sets a property, the component reacts.
This became Baseline newly available on 19 May 2026, when Firefox 151 shipped it. One caveat is doing a lot of work in that sentence: only custom properties are implemented. The specification allows style queries against ordinary CSS properties too, and no browser ships that.
What you can and can't query
CSS Conditional Rules Level 5 defines style features in three forms — a declaration, a bare property name, and a range comparison — and each can name a custom property or an ordinary CSS property. Browser support does not match that, so this is the table to keep:
| Form | Example | Shipped? |
|---|---|---|
| Custom property with a value | style(--density: compact) | Yes, in every engine |
| Ordinary CSS property | style(display: grid) | No |
| Boolean — property differs from its initial value | style(--density) | Not tracked as shipped |
| Range comparison | style(--cols > 2) | Not tracked as shipped |
@mdn/browser-compat-data records support for exactly one key here, "Style queries for custom properties". Anything else in the list above needs testing in every target browser before you rely on it, whatever the spec says.
The practical consequence: a style query asks about values you set deliberately, not about how the page is rendered. To react to display, position or a font size, set a custom property alongside the real declaration and query that.
Nothing to opt into
Size container queries require container-type: inline-size, which applies containment and creates an independent formatting context — a real layout change that you sometimes have to design around.
Style queries need none of that. The initial value of container-type is normal, and the spec is explicit that such an element "is not a query container for any container size queries or container scroll-state queries, but remains a query container for container style queries". Every ancestor is already a style container. There is no performance or layout trade-off to accept, and no property to add.
You can still name containers if you want a specific one to answer:
.theme-root { container-name: theme; }
@container theme style(--tone: quiet) {
.card { box-shadow: none; }
}The four things that catch people out
1. An element cannot query its own property
The query container is chosen from the element's ancestors. The spec says style rules can be conditioned for "a query container's flat tree descendants", so this does nothing:
/* .card sets --variant on itself — the query will never see it */
.card { --variant: promo; }
@container style(--variant: promo) {
.card { border-color: hotpink; }
}Set the property on the component root and style the parts inside it, or set it on a wrapper. This is the single most common mistake, and it fails silently.
2. Custom properties inherit, so the match spreads
Because custom properties inherit, every element below the one that set --variant has an ancestor whose computed value matches. Nested components inherit the outer context, which is usually what you want for density or tone — and exactly wrong when a promo card contains an ordinary card.
Reset the property on the inner component root so it shadows the inherited value:
.card { --variant: initial; } /* inner cards start neutral */
.card.is-promo { --variant: promo; }Note that revert and revert-layer are invalid inside a style feature's value and make the whole query false, so don't reach for those.
3. In Safari, the document element isn't a container
WebKit's implementation does not allow the document element to be a query container (WebKit bug 271040). Most of the time this goes unnoticed, because custom properties inherit: a token set in :root is also the computed value on body, and body is a perfectly good container for anything deeper in the tree.
It bites in two cases. First, when the element you're styling is body itself, because then :root is the only candidate container. Second, when you name the container on the document element:
:root { container-name: theme; --tone: quiet; }
/* no container called "theme" exists in Safari */
@container theme style(--tone: quiet) { … }Put the container name on body or a wrapper instead of the html element, and both problems go away.
4. Keep the values simple keywords
The query compares the computed value of the property on the container. For a custom property registered with @property, that's a properly computed value of the declared type. For an unregistered property — the usual case — you're comparing the declared token sequence after substitution, so keep values to plain keywords like compact, promo or dark rather than expressions you expect to be normalised.
When to use a style query instead of the alternatives
| You want to… | Use |
|---|---|
| Let context set a variant that any component inside can react to | Style query |
| Switch a component's own appearance from markup or JavaScript state | data-* attribute selector |
| Style a parent based on what's inside it | :has() |
| React to the width of the space a component occupies | Size container query |
The case for a style query is specifically the first one: the value can be set by any rule — a media query, a prefers-color-scheme block, a utility class, a line of JavaScript — and everything downstream responds without touching markup or adding a class to each component. That's the prop-drilling problem, solved by the cascade.
A style query is worse than a data attribute when the component's own state is what changes, because of the ancestor rule above, and because a data attribute is visible in the DOM when you're debugging.
Browser support
| Browser | Version | Released |
|---|---|---|
| Chrome | 111 | 7 March 2023 |
| Edge | 111 | 13 March 2023 |
| Safari | 18 | 16 September 2024 |
| Firefox | 151 | 19 May 2026 |
Firefox was the last engine to ship, which set the Baseline keystone date of 19 May 2026. Support data from @mdn/browser-compat-data 8.1.1, correct at the time of writing.
Container style queries are also a focus area of Interop 2026, meaning the cross-browser differences that remain — including the WebKit document-element limitation — are being tested and worked on this year.
There is no dependable feature query for this. @supports (container-name: test) doesn't help — Chromium supported container-name from version 105 and style queries only from 111 — so a test like that passes in browsers that ignore your query.
Write the base rule so it stands on its own instead. An unmatched style query leaves that rule in place, which is the definition of a safe fallback, and it's the reason style queries are comfortable to adopt while they're newly rather than widely available.
FAQ
Do container style queries need container-type?
No. Every element is a style query container by default, because the initial normal value of container-type still establishes one. Only size and scroll-state queries need the property.
Can I query display, colour or font-size?
Not in any shipping browser. The spec allows style features that name ordinary properties, but browser-compat-data records support only for custom properties. Mirror the value into a custom property and query that.
Why doesn't my query match when the element sets the property itself?
Because the query is evaluated against an ancestor container, never the element itself. Move the property up one level.
Do style queries hurt performance the way size queries can?
They don't impose containment or a new formatting context, which is where the layout cost of size containers comes from. Treat them as a normal cascade feature.
Sources
- CSS Conditional Rules Module Level 5 — Style Container Features — CSS Working Group Editor's Draft
container-typein CSS Conditional Rules 5 — the definition of thenormalvalue@mdn/browser-compat-data— support data and the WebKit document-element note, package version 8.1.1web-features— Baseline status and keystone date for container style queries- Interop 2026 focus areas — container style queries focus area
More to read
- Design
Design · · 6 min read
Readable text on any background: how CSS contrast-color() works
contrast-color() returns black or white, whichever contrasts more with the colour you pass it. What the spec guarantees, what it refuses to promise, and how to ship it.
- Design
Design · · 6 min read
The dialog you can open, close and style without JavaScript
A button attribute opens a modal and a pseudo-class styles it, with no script. What's Baseline, what still needs JavaScript, and the attribute Safari hasn't shipped.