Container · Chapter 5 of 7 · 6 units
Container Query Units
A component never sees the world — only the room it’s given.
Viewport units ask: how big is the window? Container query units ask a better question for component design: how big is the box I’m actually in?
This is the conceptual leap. A card in a 300px sidebar and the same card in a 700px main column live in the same viewport, so viewport units can’t tell them apart. Container query units can. They measure against the nearest query container ancestor, which means a component can finally be responsive to its own context rather than the whole page.Authors had been asking for “element queries” since the early 2010s, and container queries topped the most-wanted-feature list in the State of CSS survey year after year until they shipped. The blocker was circularity — size depends on content depends on size — and container-type is the truce that finally broke it.
cqw = 1% of the query container's width
cqh = 1% of the query container's height
cqi = 1% of the query container's inline size
cqb = 1% of the query container's block size
cqmin = the smaller of cqi and cqb
cqmax = the larger of cqi and cqb
The demo below is a single component. Drag its container wider and narrower. It reflows and rescales entirely on its own.
—1cqi —Layout —Container-aware card
This card switches from stacked to side-by-side based on its own width — not the viewport. Its title even scales with cqi.
The same card in a 300px sidebar and a 700px main column renders two different layouts — with no media queries and no knowledge of the viewport. That's the leap container queries unlock.
Setting up a query container
Container query units don’t work in a vacuum. They need an ancestor that has been declared a query container. Otherwise there’s nothing to measure against.
You opt in with container-type:
.sidebar {
container-type: inline-size;
}
container-type has three values:
inline-size— the element is a query container on its inline axis only (width, in horizontal writing). This is by far the most common choice, because querying block size requires the container to have a definite height, which most flow content doesn’t.size— query container on both axes. It contains the block axis, so it needs a definite height to be useful, which is why it’s usually only worth reaching for when you actually control the element’s height.normal— not a size query container, but still a container for style queries and a reference forcontainer-name.
You can also name containers, so a descendant can target a specific ancestor:
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
/* shorthand */
.sidebar {
container: sidebar / inline-size;
}
One refinement worth carrying: eligibility is decided per axis. An ancestor
declared container-type: inline-size can answer cqi and cqw but not cqb,
so in deep nesting cqi and cqb may end up measuring two different ancestors.
container-type isn’t entirely free, though. It establishes an independent formatting context (so floats, margins, and clearance stay contained), and with size it additionally contains the block axis, meaning the element’s height stops being derived from its content. One historical gotcha has since been fixed: early implementations also made the container a containing block for position: absolute/fixed descendants — because container-type applied layout containment — which silently trapped positioned children. A 2024 CSSWG resolution removed that requirement, so in current browsers container-type no longer anchors positioned descendants. If you need the container to be the positioning context, set position: relative (or contain: layout) on it explicitly.
cqi / cqw — inline and width
cqi (container query inline) is the workhorse. In horizontal writing it’s the container’s width; in vertical writing it follows the inline axis, exactly like the logical-vs-physical relationship between vi and vw.
cqw is the physical-width sibling. Most of the time you want cqi: it’s writing-mode aware, and inline-size is what container-type: inline-size establishes.
.card-title {
font-size: clamp(1rem, 4cqi, 1.5rem);
}
This title is 4% of its container’s inline size, clamped to a sane range. Put the card anywhere — the title sizes itself to fit.
- Component typography that should scale with its container:
clamp(1rem, 4cqi, 1.5rem) - Internal spacing/padding proportional to component width
- Reusable cards, widgets, and embeds that live in many slot sizes
- Prefer
cqiovercqwfor writing-mode safety
- Without an ancestor
container-type— the unit silently falls back tosv* - Page-level sizing where the viewport really is the reference —
vw/visay that directly - Inside an
@containercondition on the same element — a query can't test the container it lives on
240pxAa
font-size: clamp(1rem, 6cqi, 2.5rem)
Aa
same CSS, wider container
Both headlines use the exact same rule. The left one is smaller purely because its container is narrower. With cqi, a component becomes responsive to wherever it's placed — sidebar, modal, full-width — with one declaration.
cqb / cqh — block and height
cqb (container query block) measures the container’s block size; cqh is the physical-height sibling. These are far less common than cqi/cqw, for one reason: they require the query container to have a definite block size, which means container-type: size and a known height.
Most layouts don’t have a definite height (content determines it), so cqb/cqh only make sense in fixed-height regions: a full-height panel, a card with a locked aspect ratio, a slide.
- Fixed-height regions: full-height panels, slides, locked-ratio cards
- Sizing something to a fraction of a known container height
- When you've deliberately set
container-type: size
- Auto-height containers — with no definite block size these don't resolve meaningfully
- Typical component work —
cqiis usually the more practical starting point - Anywhere the container's height isn't something you control
cqmin / cqmax
These mirror vmin/vmax, but for the container instead of the viewport:
cqmin = 1% of the smaller container dimension
cqmax = 1% of the larger container dimension
cqmin is useful for things that should fit inside the container regardless of orientation: a square media area, an icon that must never overflow either axis. Like cqb/cqh, cqmin and cqmax are most meaningful when the container has both dimensions defined (container-type: size).
What happens with no container?
This is the rule that saves you from mysterious zero-sized layouts:
A
cq*unit with no query container to measure doesn’t fail: it falls back to the small viewport, matching axis for axis.
So cqi with no container behaves like svi, cqb like svb, and so on. Nothing throws; the value just stops being container-relative and quietly becomes small-viewport-relative, which can still look wrong (see the pitfall below).
The killer use case: truly portable components
Before container queries, “responsive” meant “responds to the viewport.” But a component doesn’t control the viewport — all it ever sees is the box it’s dropped into. Container query units close that gap.
A single card definition can now:
- stack on narrow, go side-by-side on wide, based on its slot, not the screen;
- scale its own headline with
clamp(1rem, 5cqi, 2rem); - be dropped into a sidebar, a modal, a grid cell, or a full-width hero without variant classes or viewport breakpoints.
That’s the real win: components become genuinely context-independent. The same markup is correct everywhere because it measures the only thing that matters to its own layout — its container.
.card-slot { container-type: inline-size; } /* the container… */
.card { /* …and the component inside it */ }
.card__title { font-size: clamp(1rem, 5cqi, 2rem); }
.card__body { padding: 4cqi; }
@container (min-width: 30rem) {
.card { display: grid; grid-template-columns: auto 1fr; }
}
Note the wrapper. The @container rule styles descendants of the query
container, never the container itself, so the element that carries
container-type and the element that reacts to the query have to be two
different boxes.
Compact reference
| Unit | 1 unit = |
|---|---|
cqi |
1% of the query container’s inline size — the workhorse |
cqw |
1% of the query container’s width |
cqb |
1% of the query container’s block size |
cqh |
1% of the query container’s height |
cqmin |
1% of the smaller container dimension |
cqmax |
1% of the larger container dimension |
| Setup | Effect |
|---|---|
container-type: inline-size |
query the inline axis — the common case |
container-type: size |
query both axes — needs a definite height |
container-type: normal |
style queries / naming only (the default) |
container-name: sidebar |
let descendants target a specific ancestor |
With no eligible query container, cq* units fall back to the small viewport (sv*).
Support and containment behavior checked against MDN and CSS Contain 3 ·