Container Query Syntax Basics
Modern component-driven architecture demands a shift from viewport-centric breakpoints to element-aware contexts. This guide breaks down the foundational syntax of the @container at-rule and sits within the broader Mastering Container Queries & Responsive Layouts guide. By establishing containment directly on UI elements, you can decouple component styling from global layout shifts and ship self-contained, reusable modules. We’ll cover the core @container at-rule, container-type and container-name properties, and the new relative units that power responsive micro-interactions.
Key Implementation Points:
- Understanding the paradigm shift from viewport to component-level responsiveness
- Establishing containment contexts without breaking layout flow
- Writing spec-compliant size and style queries
- Leveraging container-relative units for scalable UI components
Declaring the Containment Context
To query an element, you must first establish a containment context on its direct ancestor. The container-type property dictates which dimensions the browser tracks, directly impacting rendering performance and query accuracy.
container-type: inline-size: Tracks only the inline axis (width in horizontal writing modes). Highly performant, as it avoids block-size layout recalculations and is the recommended default.container-type: size: Tracks both inline and block dimensions. Triggers more frequent layout recalculations; reserve for components where height directly dictates internal layout.container-name: Assigns an explicit identifier. Crucial for targeting specific containers in deeply nested DOM trees where multiple containment contexts exist; see nesting and naming container queries for disambiguation strategies.- Ancestor Rule: Containment must be declared on a direct or indirect parent of the queried element. The browser resolves queries up the DOM tree until it finds a matching named or typed container.
When deciding between viewport and element-level responsiveness, understanding the Container vs media queries comparison clarifies when containment prevents unnecessary layout thrashing.
/* Minimal DOM structure for context */
/* <div class="card-wrapper"><div class="card-content">...</div></div> */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card-content {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1.5rem;
}
}
The @container At-Rule Structure
Once a context is established, conditional styling follows a strict, spec-compliant syntax. The @container at-rule evaluates either physical dimensions or computed CSS values, enabling highly granular component logic.
- Standard Syntax:
@container <name> (<condition>) { ... } - Size Conditions: Use standard length units (
px,rem,%) withmin-width,max-height,aspect-ratio, andorientation. - Style Queries: Evaluate custom properties (
--theme,--spacing) and computed values. Ideal for theme switching, accessibility states, and component variants without JavaScript. - Logical Operators:
and,or,notenable complex condition grouping. Always group conditions logically to improve readability and maintainability.
Structuring queries this way aligns directly with scalable Responsive Component Patterns in production workflows.
/* Style Query: Evaluates computed custom property */
@container style(--theme: dark) {
.component {
background: #1a1a1a;
color: #f0f0f0;
border-color: #333;
}
}
/* Complex Size Query with Logical Operators */
@container card (min-width: 300px) and (max-width: 600px) {
.component-header {
flex-direction: column;
align-items: flex-start;
}
}
Container Query Units & Relative Sizing
Container-relative units replace static px or rem values with dynamic scaling factors tied directly to the containment context. These units enable proportional spacing, typography, and micro-interactions that adapt fluidly to available space; the dedicated breakdown of container query units cqi and cqb covers their writing-mode behavior in depth.
cqw/cqh: 1% of container inline/block size.cqi/cqb: Logical equivalents that adapt towriting-mode(critical for RTL/vertical layouts).cqmin/cqmax: Selects the smaller or larger dimension, perfect for responsive padding, border radii, and icon scaling.
Pairing these units with fluid scaling techniques like Fluid Typography with clamp() creates self-adjusting components that scale proportionally without hard breakpoints.
.component-title {
/* Fluid typography scaling between 1rem and 2.5rem based on container width */
font-size: clamp(1rem, 2cqw + 0.5rem, 2.5rem);
/* Logical spacing that adapts to container dimensions */
padding: 1cqi;
margin-block: 0.5cqb;
border-radius: min(1rem, 2cqmin);
}
Production-Ready Syntax & Progressive Enhancement
Deploying container queries requires robust progressive enhancement. Wrap your syntax in @supports (container-type: inline-size) to ensure graceful degradation. Provide baseline styles outside the block, and optionally chain @media queries for legacy browsers. Avoid containment conflicts by ensuring container declarations don't interfere with CSS Grid/Flexbox intrinsic sizing. Modern linters (Stylelint, PostCSS) support container query validation out-of-the-box. For deployment guidelines, refer to How to use container queries in production.
/* Progressive Enhancement Pattern */
@supports (container-type: inline-size) {
.card {
container-type: inline-size;
padding: 1rem;
}
@container (min-width: 300px) {
.card {
padding: 2rem;
}
}
}
@supports not (container-type: inline-size) {
/* Baseline fallback for unsupported browsers */
.card {
padding: 1rem;
}
@media (min-width: 600px) {
.card {
padding: 2rem;
}
}
}
What each container type allows
container-type accepts three values, and the difference between them is not just which queries work but what the browser must give up to answer them. A size query needs the container's size to be known without looking at its contents — otherwise a child that changes when the container is wide could make the container narrower, which would change the child again. Containment breaks that loop by making the container's size in the queried axis independent of its children.
The practical default is inline-size. Components usually respond to how wide they are; their height follows from their content, and inline-size containment leaves that intact. Reach for size only for components whose height is already fixed by their context — a full-height panel, a dashboard tile in a fixed grid row — and remember that without an explicit block size such a container collapses to nothing. container-type: size vs inline-size walks through the collapse and its fixes.
Choosing thresholds from content, not devices
Media query breakpoints are traditionally chosen from device widths — 768 for tablets, 1024 for laptops. Those numbers mean nothing inside a container, because a component can sit in a 300-pixel sidebar on a 1440-pixel screen. Container thresholds should come from the component's own content: the width at which its layout stops working.
The method is simple. Start the component at its narrowest supported width and widen it slowly. Watch for the moment the layout is clearly wasting space or straining — a title wrapping to a single word per line, an image too small to read, a gap that has become a canyon. That width, expressed in rem or ch so it scales with the user's font size, is the threshold. Components typically need one or two such thresholds, rarely more. Drag the frame in the demo below across its two thresholds to see a card move from stacked, to side by side, to a roomy layout with a supporting column.
Expressing thresholds in rem rather than pixels has an accessibility payoff. When a user increases their default font size, a 28rem threshold moves outwards with the text, so the component switches to its stacked layout sooner — exactly what larger text needs. A pixel threshold would keep a cramped two-column layout that no longer fits its content.
Range syntax and logical combinations
Container conditions accept the same modern range syntax as media queries. (width >= 30rem) reads more clearly than (min-width: 30rem), and a bounded range such as (30rem <= width < 50rem) expresses a band in one condition instead of two. Conditions combine with and, or and not, and parentheses group them. A component that needs both enough width and a particular aspect — a wide but short banner — can write (width > 40rem) and (aspect-ratio > 3), which only a size container can answer because aspect ratio depends on height. Container Query Range and Logic Operators covers the edge cases, including why not needs its own parentheses.
How the browser finds the container
Every @container rule is evaluated against a specific ancestor. Without a name, the browser walks up from the element being styled and uses the nearest ancestor that is a size container for the queried axis. With a name, it walks up to the nearest ancestor that has that name and a suitable type. Two consequences matter in practice. First, an element can never query itself: a rule inside @container styles descendants of the container, not the container element. Second, adding container-type to an intermediate wrapper during a refactor silently changes which container unnamed queries resolve to. Naming containers used across components — card, sidebar, main — makes queries robust to that kind of change, and Nesting and Naming Container Queries shows naming conventions that scale.
Migrating from media queries
Most codebases adopting container queries already have components styled with viewport media queries. Converting them does not need to happen all at once, and a gradual path avoids regressions:
- Pick components that appear in more than one context. A card used in both a main column and a sidebar gains the most, because a single viewport breakpoint was already wrong for one of its placements.
- Add a container on the component's wrapper, not the component itself. The component's own element cannot query itself, so the container must be its parent — often a layout slot that already exists.
- Translate breakpoints into content thresholds. A
@media (min-width: 768px)rule that switched the card at a tablet width usually corresponds to a much smaller container width, because the card was never the full viewport wide. Re-derive the threshold by widening the component, as described above. - Delete the media query once the container version ships. Leaving both in place makes the two conditions fight whenever they disagree, which is exactly in the contexts that motivated the change.
Page-level layout — the overall grid of header, main and sidebar — usually stays on media queries. Those decisions genuinely depend on the viewport, and Container vs Media Queries sets out where each belongs.
Performance characteristics
Container queries are designed to be cheap. Containment means a container's size is known before its contents are laid out, so the browser can evaluate queries during a single layout pass rather than laying out twice. In practice the cost of a container query is comparable to the cost of the layout it changes. Two habits keep it that way: avoid container-type: size where inline-size will do, since size containment affects more of the layout algorithm; and avoid declaring every element on the page as a container "just in case", because each container is a boundary the browser must track. Declaring containers on component wrappers and layout slots — the places queries actually target — is both the clearest and the cheapest structure.
Debugging which container matched
When a container query does not apply as expected, the question is almost always which container the browser resolved. Chromium and Firefox DevTools mark container elements in the Elements panel and show, for a rule inside @container, the container it was evaluated against along with that container's current size. Hovering the container badge highlights it on the page. Debugging Container Queries in DevTools walks through the panels step by step.
Container units in one paragraph
Alongside the at-rule, containers expose relative units: cqi and cqb are one percent of the container's inline and block size, cqw and cqh their physical equivalents, and cqmin and cqmax the smaller and larger of the two. They resolve against the nearest container of a suitable type, falling back to the small viewport units when no container exists. The typical use is sizing that should scale continuously with the component — a heading that grows with its card, padding that breathes in wide slots — combined with clamp() so it never becomes unreadably small or absurdly large. Units and queries complement each other: queries switch between discrete layouts at thresholds, while units smooth the sizing within each layout. The dedicated guide to container query units covers the fallbacks and the traps, including why cqb needs a size container.
Browser Compatibility Matrix
| Feature | Chrome/Edge | Firefox | Safari | Notes |
|---|---|---|---|---|
container-type: inline-size | 105+ | 110+ | 16+ | Widely supported, safe for production |
container-type: size | 105+ | 110+ | 16+ | Supported, but monitor layout performance |
Container Query Units (cqw, cqi, etc.) | 105+ | 110+ | 16+ | Fully supported across modern engines |
Style Queries (@container style(...)) | 111+ | Pending | 18+ | Partial support; use @supports feature detection |
Cross-Browser Note: Inline-size containment is stable across all major engines. Full size containment and style queries require progressive enhancement strategies. Always test with @supports before shipping style-dependent logic.
Common Implementation Pitfalls & Fixes
| Issue | Root Cause | Resolution |
|---|---|---|
| Layout thrashing on resize | Applying size containment to deeply nested or frequently repainted elements | Default to inline-size. Use size only when block dimension directly impacts internal layout. |
| Fallback chains override container styles | Missing @supports nesting or incorrect cascade order | Wrap CQs in @supports, place baseline styles outside, and use higher specificity or !important sparingly if needed. |
| Inconsistent behavior in vertical writing modes | Confusing width/height with logical inline/block axes | Use cqi/cqb and inline-size/block-size for writing-mode agnostic layouts. |
| Global layout shifts instead of component adaptations | Overusing CQs for page-level routing or major structural changes | Reserve CQs for component-level micro-adaptations. Use media queries for global layout shifts. |
| Ambiguous matches in nested trees | Missing container-name when multiple containers share the same type | Always assign explicit container-name values in complex component hierarchies. |
FAQ
What is the difference between container-type: inline-size and container-type: size?inline-size establishes a query context based only on the inline dimension (width in horizontal writing modes), which is highly performant. size establishes a context for both inline and block dimensions but triggers more frequent layout recalculations, making it less suitable for frequently resized elements.
Can I use container queries with CSS Grid and Flexbox? Yes. Container queries work seamlessly with Grid and Flexbox. Declare the containment context on a parent element, and queried children can adjust their internal grid/flex layouts based on the container's dimensions rather than the viewport.
How do I handle browsers that don't support container queries?
Wrap your container query syntax in an @supports (container-type: inline-size) block. Provide a baseline layout outside the block, and optionally use @media queries as a fallback for viewport-based responsiveness in older browsers.
When should I use style queries instead of size queries? Use style queries when component appearance depends on computed CSS properties, custom properties, or inherited states rather than physical dimensions. They are ideal for theme switching, accessibility preferences, and state-driven UI variations.
DevTools Debugging Workflow
- Inspect Containment Context: Open Chrome/Firefox DevTools → Elements panel. Select the container element. In the Computed tab, verify
container-typeandcontainer-nameare applied. - Visualize Query Boundaries: In Chrome DevTools, enable
Show container query boundariesin the Layout pane (under Elements > Styles > Container Queries). This overlays a visual guide showing wherecqw/cqicalculations originate. - Simulate Container Resize: Use the Device Toolbar or manually drag the container's parent in the Elements panel. Observe
@containerbreakpoints triggering in the Styles pane. - Debug Style Queries: Toggle custom properties in the Styles panel. DevTools will highlight which
@container style(...)blocks activate based on computed values. - Performance Profiling: Open Performance tab → Record → Resize container repeatedly. Check for
Layoutspikes. If present, switch fromsizetoinline-sizecontainment or debounce JS-driven resizes.
Specification References
- CSS Containment Module Level 3: Defines
container-type,container-name, and containment semantics. W3C Spec - CSS Conditional Rules Module Level 5: Standardizes
@containersyntax, logical operators, and style query evaluation. W3C Spec - CSS Values & Units Module Level 4: Documents container-relative units (
cqw,cqh,cqi,cqb,cqmin,cqmax). W3C Spec
Related
- Container query units cqi and cqb explained — how logical container units track writing mode.
- Nesting and naming container queries — disambiguating multiple containment contexts.
- Container vs media queries comparison — when element context beats the viewport.
- Mastering Container Queries & Responsive Layouts — the parent guide tying these techniques together.
- Optimizing CSS animations for 60fps — keeping container-driven transitions on the compositor.
Related articles
More pages in the same section.