Card 1
Responds to the container.
Components that respond to their own width instead of the viewport.
Responds to the container.
Responds to the container.
Responds to the container.
The window is not changing — only the box is. That is the whole point: @container makes a component responsive to the space it is given, so the same card works in a wide main column and a narrow sidebar.
Container queries are supported in every current browser (Chrome and Edge 105+, Safari 16+, Firefox 110+). No fallback needed for a modern audience.
/* 1. Declare the containment context. Without container-type,
nothing can query this element. */
.card-list {
container-type: inline-size;
container-name: card;
}
/* 2. Children query the container, not the viewport — so the same
component adapts wherever you drop it. */
.card-list .cards {
display: grid;
grid-template-columns: repeat(1, minmax(0, 1fr));
gap: 12px;
}
@container card (min-width: 320px) {
.card-list .cards {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@container card (min-width: 560px) {
.card-list .cards {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
}
/* cqi is 1% of the container inline size — type that scales with
the component rather than the window. */
.card-list .card h3 {
font-size: clamp(0.95rem, 3.2cqi, 1.35rem);
}
A media query asks how big the window is. That was always the wrong question for a component
that might appear in a wide main column on one page and a 280px sidebar on another.
@container asks how much space the component was actually given.
A container name is optional but worth setting — it lets a query target a specific ancestor rather than the nearest one, which matters as soon as you nest containers.
inline-size for almost everything. size also contains the block axis, which means the element stops sizing to its content.
Each one is a minimum container width and the column count above it. Drag the slider under the preview to cross them — the window never changes.
You get the container declaration, the base rule and one @container block per breakpoint, plus an optional fluid font-size in cqi units.
container-type: inline-size on the parent. Without it there is no containment
context, the @container block never matches, and nothing happens — with no error
and no warning in devtools. If your container query appears to do nothing, this is why,
roughly nine times out of ten.
@container rules apply to descendants of the container, never to the container.
Styling an element based on its own size would be circular: the style changes the size, which
changes the style. The fix is always an extra wrapper — put the containment on the outside and
style the inside.
1cqi is one percent of the container's inline size, so
clamp(1rem, 4cqi, 2rem) is fluid type scoped to the component. It is what
vw should have been for component work — the same card gets smaller type in a
sidebar and larger type in a hero without a single breakpoint. Build the clamp itself in the
clamp generator and swap vw for
cqi.
Page-level decisions still belong in @media: the layout shell, print styles,
prefers-reduced-motion, prefers-color-scheme. What changes is the
ratio — most codebases that adopt container queries end up with a handful of media queries at
the top level and everything else scoped to components. The
media query generator covers that remaining half.
@container (min-width: 400px) applies when the nearest containment context is at least 400px wide — so a component adapts to the space it is given, not to the window.container-type: inline-size on the element you want to query, optionally with a container-name. Then write @container (min-width: …) rules that style its descendants. Without the container-type declaration nothing happens — that is the mistake almost everyone makes first.inline-size contains only the inline axis, so the element still grows to fit its content vertically. size contains both axes, which means the element no longer sizes to its children and needs an explicit height or it collapses. Use inline-size unless you specifically need to query height.@container rules only match descendants of the container, never the container itself — styling the container based on its own size would be circular. Wrap it: put the containment on an outer element and style the inner one.cqi is 1% of the container’s inline size, cqb 1% of its block size, and cqw/cqh the physical width and height. They are the container equivalent of vw/vh, and pair well with clamp() for type that scales with the component.prefers-reduced-motion, prefers-color-scheme. Container queries own component-level decisions. Most codebases end up with far fewer media queries and a lot more @container.