Combining Size and Style Container Queries
Size queries answer "how much room does this component have?". Style queries answer "what has the surrounding context asked this component to be?". Real design systems need both at once: a card that uses its side-by-side media layout only when its section asks for the media style and it has room for it; a promotional block that switches to its compact variant in narrow containers unless its theme demands the full treatment. This page shows how to express those combined conditions, how container resolution works when size and style come from the same or different ancestors, and the resolution trap that makes combined queries silently fail. It belongs to Style Queries & Container State in the Mastering Container Queries & Responsive Layouts guide.
Why combine them
A variant chosen by context but applied regardless of space breaks in narrow placements. A marketing team sets --card-layout: media on a section so its cards show images beside text; the same section later appears in a narrow sidebar, and the side-by-side layout squeezes every card's text into a column two words wide. The variant is a request, and requests should be honoured only when they can be.
Conversely, a size-only switch ignores intent. A card that always goes side by side above 30rem cannot be told by its context to stay stacked in a particular section. Combining the two conditions expresses the real rule: "use the media layout when asked and when there is room".
The complete implementation
The demo shows two sections with identical cards. Only the second section requests the media layout, and its cards use it only when their container is wide enough.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Size and style queries together</title>
<style>
body { font: 15px/1.5 system-ui, sans-serif; margin: 1rem; }
/* Sections publish a layout request as a custom property. */
.section--media { --card-layout: media; }
/* Each card slot is BOTH the size container and the element where the
inherited --card-layout is visible, so one rule can test both. */
.card-slot {
container: card / inline-size;
}
.card {
display: grid;
gap: 0.75rem;
padding: 1rem;
border: 1px solid #cbd5e1;
border-radius: 12px;
}
.card__media { aspect-ratio: 16 / 9; border-radius: 8px; background: #c7d2fe; }
/* Media layout only when requested AND there is room. */
@container card (width > 30rem) and style(--card-layout: media) {
.card { grid-template-columns: 12rem 1fr; align-items: start; }
.card__media { aspect-ratio: 1; }
}
</style>
</head>
<body>
<section class="section">
<div class="card-slot"><article class="card"><div class="card__media"></div><p>Default section: always stacked.</p></article></div>
</section>
<section class="section section--media">
<div class="card-slot"><article class="card"><div class="card__media"></div><p>Media section: side by side when wide.</p></article></div>
</section>
</body>
</html>
The card slot is the single container that both conditions are evaluated against. It has size containment from container-type (inside the container shorthand), and it has --card-layout because custom properties inherit from the section into the slot. One element satisfies both requirements, so one @container rule can test both.
The key technique: one rule resolves one container
A single @container rule — however many conditions it combines — finds exactly one container: the nearest ancestor that matches its name (or any container, if unnamed) and, for size conditions, has the needed containment. Every condition in the rule is then evaluated against that element.
The trap is to name a container that has only one of the two properties. If the combined rule names section, and the section is not a size container, the size condition cannot be evaluated against it and the rule never matches — even though a size container exists one level down. When the size and the style genuinely belong to different ancestors, nest two rules, each naming the container it needs:
.section { container: section / normal; } /* style queries need no size containment */
.card-slot { container: card / inline-size; }
@container section style(--card-layout: media) {
@container card (width > 30rem) {
.card { grid-template-columns: 12rem 1fr; }
}
}
Nested rules each resolve their own container, and the inner rule's styles apply only when both match. Relying on inheritance, as the main example does, is simpler when possible; nesting is the general solution. Nesting also documents intent: a reader sees immediately that the section provides the request and the card slot provides the space, which is harder to infer from a single combined condition.
Other useful combinations
The and form is the most common, but or and not cover real design rules too.
Override a size switch with a request. @container card (width > 30rem) or style(--card-layout: compact) makes cards compact in narrow containers or wherever a section explicitly asks for compact cards, even in wide containers. Dense listing pages use this to show many small cards in a wide column.
Exclude a context. @container card (width > 30rem) and not style(--card-layout: stacked) applies the wide layout everywhere there is room except sections that insist on stacking — useful for a print-like editorial section that should never go side by side.
Tiered requests. A request can carry more than two values and be combined with different thresholds: style(--density: comfortable) might need 36rem for the roomy variant, while style(--density: compact) fits at 24rem. Each tier is a separate combined rule, and the thresholds document how much space each variant needs.
When several combined rules can match the same element, their order and specificity decide the result exactly as for any other rules, so write them from least to most specific condition and keep each rule's declarations focused on what that condition changes. A component whose base styles hold the default and whose combined rules each override a small set of properties is far easier to reason about than one where several rules each restate the whole layout.
Designing the request tokens
Style-query-driven variants work best with a small, documented vocabulary of request tokens:
- Name the request, not the result.
--card-layout: mediasays what the context wants; the component decides what that means at each size. Tokens like--card-columns: 2hard-code a result the component cannot adapt. - Use keywords, not free text. Style queries compare computed values exactly, so a CMS that sometimes writes
Mediaand sometimesmediaproduces cards that sometimes match. Constrain the input. - Provide a default. Components should look right when no request is set. Querying for the variant and leaving the default unconditional keeps components usable anywhere.
- Document interactions with size. For each token, state the minimum container width at which it applies, so designers know that requesting a variant in a narrow sidebar will be silently declined.
The token approach and its theming counterpart are covered in Style Queries With Custom Properties. When the variant switch should animate — the card smoothly rearranging as a section changes its request — the transition techniques in Container-Query-Triggered Keyframe Animations apply equally to style-query-triggered changes.
Browser support
Size container queries are supported in Chrome and Edge 105+, Firefox 110+ and Safari 16+. Style queries for custom properties are supported in Chrome and Edge 111+, Firefox 151+ and Safari 18+, and combining them with size conditions in one rule is supported wherever both are. In engines with size queries but no style queries, a combined rule that contains a style() condition is not applied, so the component falls back to its default, stacked layout; see Style Query Fallbacks and Support for ways to provide a class-based alternative.
FAQ
Can one @container rule test both size and style?
Yes. Combine them with and, or and not, as in @container card (width > 30rem) and style(--layout: media). Both conditions are evaluated against the same container, so it must be a size container and have the custom property set on it or inherited.
What if the size and the style live on different ancestors?
Nest two @container rules, each naming its container. The outer rule can query a section's style and the inner rule a card's size. Each condition is evaluated against the nearest ancestor with the matching name.
Why does my combined query never match?
Usually the container used for the style condition is not a size container, or vice versa. A single @container rule resolves one container for all its conditions, so if the nearest named container has no size containment, the size part fails even though the style part would match.
Should I use a style query or a class for a variant? Use a class when the variant is set directly on the component. Use a style query when the value comes from an ancestor, a design token or a CMS setting that the component cannot see as a class, especially when it should combine with size conditions.
Related
- Style Queries & Container State — the parent guide.
- Container Query Range Syntax and Logic — the and/or/not grammar used here.
- Style Query Component Variants — building a variant set on tokens.
() vs Container Style Queries — when discovery beats a published request.
Related articles
More pages in the same section.