Style components from a custom property on their container, not from a class on themselves
@container style() checks whether a custom property on the query container has a specific value and applies rules only when that condition holds, opening up theming, density switches and design token driven components that stay completely context free. Here is where style queries genuinely help, where they still fall short, and how to fall back safely.
Table of Contents
- 1. What style queries are and how they differ from size-based container queries
- 2. The style() syntax: querying custom properties as a condition
- 3. Use case: driving theme variants from a custom property on the parent
- 4. Use case: layout density (compact/comfortable) without extra classes
- 5. Boolean-like custom properties and how they differ from real CSS booleans
- 6. Combining style queries with size queries
- 7. Inheritance, cascade, and why style queries need the custom property on the container itself
- 8. The real browser support picture and a safe fallback pattern
- 9. When style queries are the right choice, and when classes still fit better
- 10. Summary
- 11. FAQ
1. What style queries are and how they differ from size-based container queries
Container queries first became known purely as a size check: @container (min-width: 400px) lets a component react to the available space of its nearest named ancestor, independent of viewport width. Style queries extend the same underlying mechanism with a completely different kind of condition: instead of asking about dimensions, @container style(...) checks whether a specific CSS custom property on the container has a specific value, and only applies rules when that condition is met.
The key difference from a classic class is the direction the information flows: a class gets set directly on the element that should change, while a style query checks the condition on an ancestor and lets the descendants react, without those descendants ever needing to carry a class of their own. That makes style queries especially valuable for design-system components whose inner structure should not need to know which theme or context it is currently being rendered in.
2. The style() syntax: querying custom properties as a condition
The basic syntax is @container style(--property: value) { ... } and checks the computed value of the given custom property on the nearest container defined as a query container through container-type. Unlike size queries, a style-query container currently does not need an explicit container-type: size in most implementations, because style queries work independently of any size calculation.
It matters that the condition compares the computed string value exactly, not numerically greater or smaller. A custom property with the value dark only satisfies a condition that asks for exactly dark, not a range or a substring. That makes style queries syntactically simpler than size queries, while also limiting how conditions can be phrased.
.card-container {
--theme: light;
container-name: card;
}
@container card style(--theme: dark) {
.card {
background-color: #18181b;
color: #f4f4f5;
border-color: #3f3f46;
}
}
3. Use case: driving theme variants from a custom property on the parent
The most obvious use case for style queries is component level theming: a custom property like --theme is set once on the outer container, for example depending on which area of the page a card component is embedded in, and every card inside that container reacts automatically, without the card itself needing a theme class or any JavaScript logic passing the context down.
That is especially valuable in design systems with many reused components, because the component itself stays completely context free: the same .card rule works identically whether it lands in a light marketing area or a dark dashboard area of the application, as long as the respective parent area sets the matching custom property. That decouples the component definition from the place it actually gets used.
/* Marketing area sets a light theme on its section container */
.marketing-section { --theme: light; container-name: theme-scope; }
/* Dashboard area sets a dark theme on its section container */
.dashboard-section { --theme: dark; container-name: theme-scope; }
/* The card component itself never needs a theme class */
@container theme-scope style(--theme: dark) {
.card { background: #1f2937; color: #e5e7eb; }
}
@container theme-scope style(--theme: light) {
.card { background: #ffffff; color: #111827; }
}
4. Use case: layout density (compact/comfortable) without extra classes
A second practical use case is controlling layout density, for example in tables or lists with a switchable compact versus comfortable mode. Instead of maintaining a separate CSS class for every density variant on every affected child element, a single custom property on the shared container is enough, and every row, cell and padding value reacts consistently to the same value.
This approach mainly reduces the number of places where density logic would otherwise have to be duplicated. Instead of tagging ten different child elements individually with .compact modifier classes, the density decision is made in exactly one place and propagated to every affected rule through style queries, which noticeably improves maintainability and consistency.
.data-table-wrapper {
--density: comfortable;
container-name: table-density;
}
@container table-density style(--density: compact) {
td, th { padding-block: 0.25rem; font-size: 0.8125rem; }
tr { line-height: 1.2; }
}
@container table-density style(--density: comfortable) {
td, th { padding-block: 0.75rem; font-size: 0.875rem; }
tr { line-height: 1.5; }
}
5. Boolean-like custom properties and how they differ from real CSS booleans
CSS has no dedicated boolean data type, which is why style queries for seemingly binary states usually work with string values like true/false or an explicit single value convention. The specification does allow a shorthand that treats a set, non-empty custom property as true, but support for that shorthand is still more inconsistent across browser engines than the core feature itself.
In practice it is therefore more robust to always use an explicit string value for boolean states and phrase the condition unambiguously, such as @container style(--collapsed: true), instead of relying on the mere existence of the property. That makes the code marginally more verbose but noticeably more predictable across different browser versions.
6. Combining style queries with size queries
Style queries and classic size queries are not mutually exclusive and can be combined in a single @container rule by joining the size condition and the style() condition with and. That allows rules like only when the container is at least 500px wide and the dark theme is active at the same time, which would be considerably more cumbersome to express with plain media queries or plain classes alone.
This combination is especially valuable for responsive design systems that need to react to both available space and thematic context, for example a component that shows a reduced layout in a narrow, dark context but an expanded layout with extra metadata in a wide, dark context. A single combined query covers that case without nested media queries or extra JavaScript logic.
@container sidebar (min-width: 500px) and style(--theme: dark) {
.widget {
grid-template-columns: 1fr 1fr;
background: #111827;
}
}
7. Inheritance, cascade, and why style queries need the custom property on the container itself
An important limitation concerns the cascade: style queries evaluate the computed value of a custom property on the container element itself, not on the querying descendant. That means a custom property that only reaches the container through inheritance from an even more distant ancestor is, in many implementations, recognized more reliably when it is set explicitly on the element defined as the query container, rather than relying purely on implicit inheritance chains.
In practice that means: anyone setting a custom property high up in the document tree and expecting every arbitrary query container defined further down to automatically see it should test that in every target browser. The more robust approach is to set the relevant custom property directly on the element that also acts as the named query container, instead of relying on multi-level inheritance.
8. The real browser support picture and a safe fallback pattern
Style queries are one of the newer additions to the container query specification, and browser support noticeably lags behind plain size-based container queries, which are now broadly available. Chromium based browsers have supported style() for custom property conditions the longest, while other engines still support it only experimentally or not at all, which makes practical use today a deliberate progressive enhancement decision.
The safe fallback pattern defines base styles outside any style query and lets @container style(...) make only additional, non-critical adjustments. A browser without support ignores the entire rule and shows the base styles, a supporting browser gets the refined variant, but no user ever sees a broken or incomplete layout.
/* Base styles work everywhere, with or without style() support */
.card {
background: #ffffff;
color: #111827;
border: 1px solid #e5e7eb;
}
/* Progressive enhancement: only refines the theme where supported */
@supports (container-type: inline-size) {
@container theme-scope style(--theme: dark) {
.card {
background: #18181b;
color: #f4f4f5;
border-color: #3f3f46;
}
}
}
9. When style queries are the right choice, and when classes still fit better
Style queries pay off especially where a component should stay context free and the context information already exists as a custom property or has to be set anyway, for example for design tokens. For simple, binary states that can easily be expressed with a single extra class on the affected element itself, a classic class often remains the more pragmatic and cross-browser reliable choice.
The decision between a style query and a class is ultimately a question of responsibility: style queries fit when the context comes from outside and the component itself should not need to know about it. Classes fit when the state is known directly on the affected element and no extra indirection through a container is needed. Both techniques are not mutually exclusive but complement each other depending on the architecture.
| Criterion | Style query (@container style()) | Classic class | Recommendation |
|---|---|---|---|
| Browser support | Limited, mainly Chromium | Universal | Guard with @supports |
| Component context freedom | High, no knowledge of context needed | Low, class must be set | Style query for design-system components |
| Combining with a size query | Directly possible via and | Not applicable | Style query for combined conditions |
| Debugging effort | Higher, value comes from the container | Lower, visible directly on the element | Class for simple, local state |
| Typical use | Theme, density, design tokens | Local UI state (open/active) | Choose based on where the state originates |
Mironsoft
Modern CSS, layout architecture and rendering performance
CSS that stays maintainable instead of breaking with every change?
We review existing stylesheets for specificity chaos and layout thrashing, then build a CSS architecture with cascade layers, custom properties and modern layout primitives that still makes sense after the tenth feature.
CSS Audit
Systematically uncovering specificity issues, cascade conflicts and unused selectors.
Architecture Refactoring
Introducing cascade layers, custom properties and design tokens cleanly.
Performance Tuning
Fixing layout thrashing, expensive selectors and rendering bottlenecks.
10. Summary
Container Style Queries: The Essentials at a Glance
Core idea
@container style(--property: value) checks custom property values on the query container instead of dimensions, letting descendants react while staying context free.
Strongest use case
Theming and layout density for reusable design-system components without theme classes on every child element.
Limitation
Boolean states should use explicit string values, and the custom property ideally belongs directly on the query container.
Browser reality
Support lags behind size queries, so always define base styles outside the style query and refine progressively.