Prompt and context
A card component appears in the main column, a sidebar, and a grid. Explain how CSS container queries differ from media queries, write CSS that changes the layout from the card container's width, and describe containment, scope, fallback, and testing.
Media queries evaluate viewport or device features; container queries evaluate an ancestor query container's size, style, or scroll state. GreatFrontEnd's CSS interview guide lists container queries as an interview question, Hack Frontend's HTML/CSS question list covers the topic, and MDN documents container-type, @container, and container query units. This question is not tied to a specific company.
What interviewers assess
Interviewers want the candidate to identify the response target before writing CSS. A strong answer explains that container-type: inline-size establishes a size-query context, that query rules style descendants rather than the container itself, that the nearest eligible ancestor is used by default, and why different card widths need container queries even at one viewport width.
They also check understanding of size-containment side effects, named-container boundaries, browser fallback, and testing. Merely replacing @media with @container without establishing a container or handling nesting will not work reliably.
Clarifying questions
- What should drive the response? This prompt responds to the card's inline size, not the whole viewport.
- Are style or scroll-state queries required? No; this answer focuses on size queries and treats the others as extensions.
- What is the browser support target? Confirm it first, then keep a usable base layout and fallback.
- Can component boundaries be nested? If so, use
container-nameto make the intended boundary explicit.
30-second answer
“Media queries observe the viewport and suit page-level breakpoints; container queries observe a component's ancestor and suit reusable components placed in different slots. I set container-type: inline-size on the card list, then use @container to switch the card layout at an inline-size threshold. The query styles descendants and uses the nearest eligible container by default; I use container-name for complex nesting. I provide base styles and an @supports fallback, then keep the viewport fixed while resizing the parent in the main column, sidebar, grid, and an unsupported browser.”
Step-by-step answer
Step 1: Compare response target and scope
| Dimension | Media query | Container query |
|---|---|---|
| Responds to | Viewport, device, or user preference | Ancestor query container size, style, or scroll state |
| Typical scope | Page-level layout | Internal layout of reusable components |
| Setup | @media reads media features directly | Ancestor sets container-type; descendants use @container |
| Component behavior | Instances often share viewport breakpoints | Each instance responds to its allocated width |
Media queries are a direct fit for navigation, page columns, printing, and user preferences. Container queries solve the case where a component does not know how wide its slot will be: a main column and sidebar can use different layouts at the same viewport width.
Step 2: Establish a size-query context
.card-list {
container-type: inline-size;
container-name: card-list;
}
.card {
display: grid;
gap: 0.75rem;
}
@container card-list (inline-size > 40rem) {
.card {
grid-template-columns: 8rem 1fr;
align-items: center;
}
}inline-size establishes an inline-axis size-query container and works well when writing modes matter. size establishes stronger two-axis containment and should be chosen only when block-axis queries are needed. normal does not establish a size-query context. The condition is written in @container, and the matching rule applies to descendants.
Step 3: Understand nearest containers, names, and units
Without a name, the browser finds the nearest eligible ancestor; nested components can therefore read the wrong boundary. A name makes intent explicit: @container card-list (...) matches only the container named card-list. Container query length units such as cqw and cqi scale from the query container's width or inline size, but should be combined with sensible minimums and maximums so tiny containers do not produce unreadable text.
A query must not make the container change its own size based on its result, or a feedback loop could occur; containment and scoping rules prevent that loop. Size containment can also change automatic sizing, so check empty content, unloaded images, and content-dependent heights.
Step 4: Design fallback and progressive enhancement
.card {
display: block;
}
@supports (container-type: inline-size) {
.card-list {
container-type: inline-size;
}
@container (inline-size > 40rem) {
.card {
display: grid;
grid-template-columns: 8rem 1fr;
}
}
}The base layout should remain readable without container queries. A media query can provide page-level fallback, but a viewport breakpoint cannot accurately represent every sidebar or grid-cell width. When the target browser lacks the tested capability, it should keep the base styles without losing core content or interaction.
Step 5: Test scenarios and boundaries
Keep the browser viewport fixed. Put the same component in a main column, sidebar, and two-column grid, then change only the parent width and verify that the card responds to the container rather than the viewport. Test nested named containers, different writing modes, images before and after loading, very narrow containers, dynamic slots, and a browser without container-query support. Also verify that the component does not depend on one page's DOM depth, which could silently select the wrong container when reused.
Acceptable implementation boundaries
An answer does not need to cover every query type, but it must accurately distinguish the viewport from an ancestor container and make the example runnable with a real query context. If the candidate chooses size, style queries, or scroll-state queries, they should explain the extra containment effects, browser target, and test boundaries. When support is uncertain, the base layout should be the safe default.
Common mistakes and counterexamples
- Forgetting
container-type:@containerwithout a query context does not respond to size. - Making the container its own query target: stable queries style descendants; self-sizing from the result can create a loop.
- Using
sizeby default: two-axis containment can affect automatic sizing; chooseinline-sizefor a horizontal-only need. - Testing only a wide screen: a sidebar and main column differ at one viewport width, so resize the parent with the viewport fixed.
- Skipping a base fallback: unsupported browsers need a usable base layout before enhancement.
Follow-ups and responses
When would you still use media queries?
Use media queries when navigation, page columns, printing, or user preferences depend on viewport or media features. Use container queries when a component's internal breakpoint depends on its parent.
How do you choose inline-size versus size?
Choose inline-size when the component responds only to available horizontal space. Consider size when both block and inline axes are required, then verify automatic sizing, empty content, and post-image-load behavior.
How do you handle unsupported browsers?
Provide a base single-column or fluid layout and progressively enhance it with @supports (container-type: inline-size). Media queries can supplement page-level behavior, but they cannot replace every container-level breakpoint.