← Read

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:

FormExampleShipped?
Custom property with a valuestyle(--density: compact)Yes, in every engine
Ordinary CSS propertystyle(display: grid)No
Boolean — property differs from its initial valuestyle(--density)Not tracked as shipped
Range comparisonstyle(--cols > 2)Not tracked as shipped
drafts.csswg.org ↗CSS Conditional Rules Module Level 5: Style Container FeaturesThe normative definition of style features, including the forms browsers have not shipped. Source: CSS Working Group

@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 toStyle query
Switch a component's own appearance from markup or JavaScript statedata-* attribute selector
Style a parent based on what's inside it:has()
React to the width of the space a component occupiesSize 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

BrowserVersionReleased
Chrome1117 March 2023
Edge11113 March 2023
Safari1816 September 2024
Firefox15119 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

More to read