A container query is a CSS feature that applies styles based on the dimensions or computed style of an ancestor element, called the container, instead of the dimensions of the browser viewport. In CSS it is written with the @container at-rule and enables component-level responsive design: a card, widget or navigation block adapts to the space it actually receives.
Media queries answer how wide the screen is; container queries answer how wide the box is that this component sits in. The difference matters because the same component may appear in a narrow sidebar and a wide main column on the same page at the same time, where a viewport-based rule cannot style both correctly.
How container queries work
Using a container query takes two steps. First, an ancestor is declared a query container with the container-type property, optionally named with container-name. Second, descendant rules are wrapped in an @container block with a condition, such as @container card (min-width: 400px), and apply only when the nearest matching container meets it.
Declaring a container applies size containment: the browser lays out the container without considering its children’s size along the queried axis. This breaks the circular dependency in which a child’s styles change the container’s size, which in turn changes which styles apply. It is also why a container cannot query its own size to style itself, only its descendants.
| Value | Queryable features | Containment effect | Typical use |
|---|---|---|---|
| inline-size | Inline size, usually width | Inline-axis size containment | Most components, the default choice |
| size | Width, height and aspect ratio | Containment on both axes | Elements with an explicit height |
| normal | Style queries only | No size containment | Default value of every element |
Because size contains both axes, an element with container-type: size and no explicit height collapses to zero height. For this reason, inline-size is the practical default in almost all layouts.
Size queries, style queries and container units
The @container rule supports several kinds of conditions. Size queries test dimensions and are the most widely used. Style queries test computed values, currently most reliably the values of custom properties, such as whether a theme variable equals dark. Newer scroll-state queries test conditions such as whether a sticky element is stuck.
| Query kind | Example condition | Support status |
|---|---|---|
| Size query | (min-width: 30em) or (width > 400px) | Supported in all major browsers since 2023 |
| Style query | style(--variant: compact) | Supported for custom properties, less uniform |
| Scroll-state query | scroll-state(stuck: top) | Newer, limited support |
Container query length units size values relative to the query container: cqw and cqh are 1% of its width and height, cqi and cqb are 1% of its inline and block size, and cqmin and cqmax use the smaller or larger dimension. Combined with clamp(), these units allow fluid typography that scales with the component rather than with the screen.
Container queries compared with media queries
Container queries complement media queries rather than replace them. Media queries still handle page-level layout and user preferences, while container queries handle how individual components respond to their slots.
| Aspect | Container query | Media query |
|---|---|---|
| Condition based on | Ancestor container size or style | Viewport and device features |
| Scope | Individual components | Whole page layout |
| Setup required | Declaring a container | None |
| User preferences | Not available | prefers-color-scheme, prefers-reduced-motion |
| Reuse across contexts | Component adapts anywhere | Same rules regardless of slot |
| Containment side effects | Yes, size containment | None |
A useful division of labor is: media queries define the page grid, for example how many columns exist at a breakpoint, while container queries define component variants, for example whether a card shows its image beside or above the text. Layout tools such as CSS grid and flexbox work inside both approaches.
Practical patterns for container queries
Container queries are most valuable in design systems and component libraries, where authors cannot know every place a component will be used. The table maps common situations to an appropriate pattern.
| Situation | Recommended approach | Reason |
|---|---|---|
| Card reused in sidebar and main area | Size query on a wrapper | Layout follows available width |
| Overall page columns | Media query | Depends on the viewport |
| Theme or variant switch | Style query on a custom property | Avoids extra modifier classes |
| Heading size within a widget | cqi units with clamp() | Scales smoothly with the widget |
| Simple wrapping list of items | Flexbox or grid auto-fit | Intrinsic layout needs no query |
A reliable convention is to place the container on a dedicated wrapper around the component, not on the component itself. This keeps containment side effects predictable and lets the component query the space it has been given.
Limitations and common mistakes
Container queries introduce new failure modes that developers used to media queries often miss. Most stem from misunderstanding size containment.
- Querying the container itself: styles inside
@containerapply to descendants only, never to the container element. - Collapsed height:
container-type: sizewithout an explicit height removes intrinsic height. - Shrink-to-fit parents: containers inside floats or inline-block parents may size unexpectedly because content no longer drives width.
- Unnamed nested containers: without
container-name, a query matches the nearest ancestor container, which may not be the intended one. - Over-querying: many layouts adapt naturally with intrinsic grid and flexbox, without any query.
A plain-language summary
A container query lets a piece of a web page adjust to the box it sits in, not to the whole screen. Think of a piece of furniture that rearranges itself to fit whichever room it is placed in.
Before container queries, designs could only react to the size of the entire browser window. Now a product card can look one way in a narrow side column and another way in a wide area. The card decides based on its own space.
An example from practice
A news site uses one article teaser component in three places: a wide homepage lead slot, a three-column grid and a narrow sidebar about 300 px wide. With media queries alone, the sidebar teasers inherited the desktop layout and squeezed their images to unreadable thumbnails.
The front-end team wraps each teaser in an element with container-type: inline-size and a container name of teaser. Below 350 px of container width, the teaser stacks its image above a short headline; between 350 and 600 px it places a small image beside the text; above 600 px it shows a large image, summary and author line. Headline size uses clamp() with cqi units.
The same component now renders correctly in every slot without page-specific overrides, and about 120 lines of context-specific CSS are deleted.