In CSS, how do you make an element a query container named card and apply styles only when that container is at least 400px wide?
answer
- two declarations plus one at-rule
- container-type first, name optional
- shorthand order is name then type
- styles land on descendants
- min-width or the range form
basics
~20 sSet container-type: inline-size and container-name: card on the wrapper, or the shorthand container: card / inline-size, then put the rules inside @container card (min-width: 400px). Those rules style the container's descendants, not the container itself.
solid answer
~40 sTwo declarations and one at-rule. On the wrapper you write `container-type: inline-size` to make it queryable and `container-name: card` to label it, or the shorthand `container: card / inline-size`. Then the component's rules go inside `@container card (min-width: 400px) { … }`. The name is optional — an unnamed `@container (min-width: 400px)` matches the nearest ancestor query container — but naming is useful when containers nest and you want to skip past the closest one. Conditions also accept range syntax, so `@container card (width >= 400px)` is equivalent. The critical detail is directionality: the rules inside the block apply to **descendants** of the container, never to the container element itself, so `container-type` belongs on a wrapper and the selectors inside target the children.
code
css · 15 lines.card-slot {
container: card / inline-size;
}
.card {
display: grid;
gap: 1rem;
}
@container card (min-width: 400px) {
.card {
grid-template-columns: 8rem 1fr;
align-items: start;
}
}go deeper
Be able to write the pattern from memory: container-type on a wrapper, optional container-name, then an @container block whose selectors target the children.
Explain when a name is worth adding, how an unnamed query resolves to the nearest ancestor container, and why the range syntax and min-width forms are interchangeable.
Show where the container wrapper belongs in a component's markup contract so the component stays portable, and how you detect support or degrade when the at-rule is ignored.
Own the naming vocabulary across a design system — which slots are containers, what they are called, and how nested containers are kept from making component styles unpredictable.
## The three pieces A working container query always has three parts: an element that opts in as a container, an optional name for it, and an `@container` rule whose selectors target that container's descendants. ```css /* 1 + 2: the wrapper opts in and takes a name */ .card-slot { container-type: inline-size; container-name: card; } /* 3: the component's rules, conditional on the container's width */ @container card (min-width: 400px) { .card { grid-template-columns: 8rem 1fr; } } ``` The shorthand collapses the first two declarations, with the name before the slash and the type after it: ```css .card-slot { container: card / inline-size; } ``` The order matters and is easy to invert from memory — it is `name / type`, matching the way `@container <name> (<condition>)` reads. ## container-type: what to pass - `inline-size` — queryable on the inline axis (width in a horizontal writing mode). This is what you want almost always. - `size` — queryable on both axes; only sound when the element has a definite size in both, otherwise it collapses. - `normal` — the default; not a size query container. ## The name is optional Without a name, `@container (min-width: 400px)` resolves against the **nearest ancestor query container** of each element the inner selectors match. That is usually what you want, and unnamed queries keep components portable — the component does not need to know what its slot is called. Names earn their keep when containers nest. Consider a card container inside a page-region container: an unnamed query inside the card will always hit the card. If a rule needs the outer region's width, name the containers and query the outer one explicitly: ```css .region { container: layout-region / inline-size; } .card-slot { container: card / inline-size; } @container layout-region (min-width: 60rem) { … } /* skips past the card */ ``` A container may carry several names (`container-name: card sidebar-slot;`), which lets one element answer to more than one query vocabulary. ## Writing the condition The condition grammar mirrors media queries. Both of these are the same query: ```css @container card (min-width: 400px) { … } @container card (width >= 400px) { … } ``` Range syntax also supports two-sided ranges — `@container card (400px <= width < 700px)` — and conditions combine with `and`, `or` and `not`, plus parentheses for grouping. Because the queried axis is inline under `container-type: inline-size`, height-based conditions such as `min-height` will not match; those require `container-type: size` on a container with a definite height. Inside a container query you can also use container-relative length units, which resolve against the query container rather than the viewport; they are a topic of their own but they pair naturally with these rules. ## The direction of application This is the part that trips people up. The block styles **descendants of the container**, not the container. This does not work: ```css /* broken: .card is the container and can never match its own query */ .card { container-type: inline-size; } @container (min-width: 400px) { .card { grid-template-columns: 8rem 1fr; } } ``` An element matching its own size query would create a circular dependency — the rule could change the width the query just measured — so the specification simply excludes it. The fix is structural: keep a wrapper as the container and style the component inside it. ```html <div class="card-slot"> <article class="card"> … </article> </div> ``` In practice, most component libraries add exactly one wrapper element per placement slot for this reason, and the component itself carries only `@container` rules — which is what makes it portable across pages. ## Feature detection If you need to branch on support, `@supports (container-type: inline-size) { … }` is the correct test. In most products this is no longer necessary; the usual approach is to write the narrow layout as the base styles so an unsupporting browser simply keeps it.
- What happens if you omit container-name and just write @container (min-width: 400px)?The query resolves against the nearest ancestor query container of whatever the inner selectors match. That is the common case and keeps a component portable, because it does not have to know the name of the slot it was placed in. Names are only needed to reach past a closer container to an outer one.
- Can one element be given more than one container name?Yes — `container-name` accepts a space-separated list, such as `container-name: card sidebar-slot;`. The element then matches queries written against either name. It is useful when the same wrapper participates in a component-level vocabulary and a layout-level one.
- Why does @container card (min-height: 300px) never match a container declared with container-type: inline-size?Inline-size containment only makes the inline axis queryable, so block-axis conditions cannot be evaluated and the query does not match. Querying height requires `container-type: size`, which contains both axes — and that is only safe when the container has a definite height, otherwise it collapses.
saying these in an interview costs you the question
- Writes the shorthand as container: inline-size / card
- Applies the @container rules to the container element itself
- Omits container-type and expects @container to work
- Thinks container-name alone makes an element queryable
- Queries min-height against an inline-size container