Media queries answer 'how big is the viewport'. Container queries answer the question components actually need answered: 'how much space do I have right now, wherever I happen to be placed'.
1Why Viewport-Based Responsiveness Falls Short For Components
A media query only ever knows one thing: the browser viewport's size. That's the right signal for page-level layout decisions (should the sidebar collapse), but it's the wrong signal for a reusable component, which might be rendered in a 300px sidebar widget or a 900px main content area on the very same page, at the very same viewport width.
Before container queries, working around this meant either giving up on true component reusability (hardcoding different markup per placement) or resorting to JavaScript-measured ResizeObserver hacks just to fake what should be a CSS-native capability.
2Declaring And Querying A Container
Two steps are required. First, an ancestor opts in with container-type: inline-size (tracking width) or size (tracking both axes, with layout implications). Optionally add container-name to target that specific container by name when nested containers exist. Second, descendants use @container with the same condition syntax as @media — min-width, max-width, aspect-ratio — but evaluated against the nearest matching container instead of the viewport.
A subtlety worth internalizing: container-type establishes a new containment context, which has a real, if usually minor, layout implication — the container becomes a query boundary its own size can't query against (to prevent circular dependencies).
3Container Units For Fluid, Component-Relative Scaling
Beyond conditional rules, container queries add a family of relative units mirroring viewport units: cqw/cqh (1% of container width/height), cqi/cqb (1% of container inline/block size), and cqmin/cqmax (the smaller/larger of the two). Combined with clamp(), these produce component-scoped fluid typography that scales smoothly with the container's actual size rather than jumping at fixed breakpoints.
This matters most for design systems: a single .card__title rule can now scale correctly whether the card is rendered small in a grid or large in a hero layout, without any conditional rule needing to be written at all.
4Step-by-Step Breakdown
Responsive To The Container, Not The Screen. Media queries respond to the viewport — but a component doesn't know or care how big the browser window is; it cares how much space its own container gives it. Container Queries close that gap, letting a card render its 'narrow' layout in a 300px sidebar and its 'wide' layout in a 900px main column, on the exact same page, at the exact same viewport size.
Declaring A Containment Context. Before an element's children can query its size, the element itself must opt in via container-type. Setting container-type: inline-size tells the browser to track that element's inline-axis size and expose it to descendant @container rules — nothing is queryable by default.
Establishing A Container. What CSS property must an ancestor element declare before its descendants can use @container queries against its size?
- →container-type
- →position
- →display
Querying With @container. Once a containment context exists, descendants use @container exactly like @media, but measuring the nearest named ancestor container instead of the viewport. The same card component can now define genuinely different layouts based purely on the space it's actually given.
Container Query Syntax. A .card inside a 320px-wide sidebar and the same .card inside a 900px-wide main column both exist on one page at one viewport width. Can they render differently using @container?
- →No — media queries would be required for that
- →Yes — each container query evaluates against its own nearest container's actual size
- →Only on mobile viewports
Container Query Units: cqw, cqh, cqi. Alongside conditional rules, container queries introduce container-relative units — cqw (1% of container width), cqi (1% of container inline-size), and others — letting values like font-size scale fluidly relative to the container's size rather than the viewport's.
Container Query Units. What does 5cqi mean when used inside a container context?
- →5% of the viewport width, same as 5vw
- →5% of the nearest container's inline-size
- →5% of the root html element's font-size
Component Responsiveness Unlocked. You now know how to opt an element into a containment context, write @container rules that respond to that context's actual size, and use container-relative units for fluid, component-scoped sizing. This is the feature that finally makes 'truly reusable responsive components' possible in CSS.
Declare A Query Container. container-type: inline-size opts an element into being a query container for its children's container queries.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Container-Relative Typography Must Still Respect User Zoom And Text-Size Preferences
Pairing cqi units with clamp()'s pixel-based min/max bounds can override a user's browser text-size preference; prefer rem-based bounds inside clamp() so container-driven scaling still respects accessibility zoom settings.
2Container Queries Don't Change Focus Order, But Layout Shifts They Trigger Can Disorient Keyboard Users
When a container query drastically reorders a component's visual layout at certain sizes, verify the underlying DOM order (and therefore tab order) still makes logical sense at every container width.
SEO Implications
- 1
Container Queries Reduce Duplicate Component Variants, Shrinking Total CSS Payload
Before container queries, teams often shipped separate 'compact' and 'full' component variants to handle different placements; a single container-queried component can replace both, reducing the CSS bundle.
- 2
True Component Reusability Improves Development Velocity On Content-Heavy Sites
Faster, more reliable component reuse across different page templates indirectly supports faster iteration on content structure, which correlates with better crawl and indexing efficiency for large sites.
Best Practices
Default To container-type: inline-size Unless You Specifically Need Block-Axis Queries
inline-size avoids the layout containment side effects of size containment, which forces the container to lay out as if empty until its content is measured — a subtlety that can introduce visual flicker if misapplied.
Name Containers Explicitly Once You Have More Than One Nested Containment Context
Without container-name, an @container rule matches the nearest ancestor container of any kind, which becomes ambiguous and error-prone once containers are nested inside other containers.
Frequent Bugs
An @container rule never seems to match, even though the container is clearly wide enough.
The ancestor element is missing container-type entirely — @container rules only evaluate against elements that have explicitly opted into a containment context.
A component briefly flashes with incorrect sizing before settling into its correct container-queried layout.
container-type: size was used instead of inline-size, forcing the browser to lay out the container as empty first. Switch to inline-size unless block-axis containment is genuinely required.
Real-World Examples
A Card That Adapts To Sidebar vs Main Column
A single .card component used both in a 300px sidebar and a 900px main content area, rendering a compact stacked layout in the former and a horizontal layout in the latter.
.layout-region { container-type: inline-size; }
@container (min-width: 500px) {
.card { display: grid; grid-template-columns: 140px 1fr; }
}