Container Queries and Subgrid
Two modern layout features that fix long-standing gaps. Container queries (@container) style an element by the size of its container rather than the viewport — true component-level responsiveness, so the same card adapts whether it’s in a wide main area or a narrow sidebar. Subgrid lets a nested grid align to its parent’s tracks, so content in sibling cards lines up across cards. Both are baseline-available in current browsers.
What problem do container queries solve that media queries can’t?
Media queries respond to the viewport, but a reusable component doesn’t know how wide it will be rendered — it depends on where it’s placed. Container queries respond to an ancestor container’s size, so a component is responsive to its own context:
.card-wrap { container-type: inline-size; } /* establish a query container */
@container (min-width: 400px) {
.card { grid-template-columns: 120px 1fr; } /* side-by-side when the container is wide */
}Drop that card in a 320px sidebar → stacked; in an 800px main → side-by-side. No viewport breakpoints, no knowing the layout in advance.
How do you set up a container, and what’s container-type?
container-type: inline-size— query the width only (most common; height stays content-driven).container-type: size— query width and height (requires the container to have a definite size, or it collapses).container-type: normal— not a size container (still usable for style/container-name).container-name: card+@container card (…)— name containers to target a specific ancestor.
Shorthand: container: card / inline-size;.
What are container query units?
Length units relative to the query container: cqw/cqh (1% of container width/height), cqi/cqb (inline/block size), cqmin/cqmax. Useful for fluid type/spacing scaled to the component:
.card h2 { font-size: clamp(1rem, 5cqi, 2rem); } /* scales with the card, not the viewport */What is subgrid and what does it fix?
A nested grid normally has its own independent tracks. grid-template-columns: subgrid (or -rows) makes the child grid adopt the parent’s track lines, so descendants align across separate grid items:
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.card { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
/* each card's title / body / footer align on shared rows across all cards */Without subgrid, each card’s internal rows size independently and titles/footers don’t line up when content length differs. Subgrid is the clean fix for “make these card sections align.”
Container queries vs media queries — do you replace one with the other?
They’re complementary. Media queries still suit page-level layout (overall columns, navigation mode) and non-size conditions (prefers-color-scheme, prefers-reduced-motion, print). Container queries suit component-level adaptation. Modern systems use media queries for the shell and container queries for reusable components.
Gotchas
- You can’t query the element itself —
@containerqueries the nearest ancestor container, so you need a wrapper element to be the container (a component can’t be its own size container for its root). Plan one wrapper per queryable component. container-type: sizeneeds a definite height or the container has zero block size and collapses;inline-sizeis the safe default.container-typeestablishes containment (layout/style/inline-size) — it creates a new containing context, which can affect things likeposition: fixeddescendants and%heights. Mostly fine withinline-size.- Subgrid only inherits tracks in the axis you set —
subgridon rows doesn’t affect columns; set both if needed. The gap can be inherited or overridden. - Naming collisions — unnamed
@containermatches the nearest container of any name; name containers when nesting to avoid querying the wrong ancestor.
Related
Further reading
- MDN — CSS container queries: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_containment/Container_queries
- MDN — Subgrid: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout/Subgrid
- web.dev — Container queries: https://web.dev/blog/cq-stable
Interview angle 4
- “What problem do container queries solve that media queries do not?” - a
component that must adapt to the space it is given rather than to the
viewport. The same card in a narrow sidebar and in a wide main column needs
two layouts, and a media query cannot tell those apart because the viewport is
identical.
- Follow-up: “Why can’t an element query itself?” - because its own size
would then depend on the rule that depends on its size. You declare
container-type: inline-sizeon a wrapper and query that, which breaks the circularity.
- Follow-up: “Why can’t an element query itself?” - because its own size
would then depend on the rule that depends on its size. You declare
- “What do container query units buy you?” - type and spacing that scale
with the component rather than the page.
1cqiis one percent of the container’s inline size, so a card’s heading stays proportionate wherever the card is placed, with no breakpoint at all. - “Two cards side by side have sections that do not line up. What fixes it?” -
subgrid. Each card’s internal grid adopts the parent’s tracks instead of defining its own, so titles, bodies and footers align across siblings. Before subgrid this needed fixed heights or JavaScript measurement. - “So do container queries replace media queries?” - no, they split the job.
Media queries own the page shell and everything that is not about size -
prefers-color-scheme,prefers-reduced-motion, print. Container queries own reusable components, which is exactly where the viewport was the wrong thing to ask about.