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.

Demo — Resize the container: drag its right edge, or use the slider
Container width 1cqi Layout
Width

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 for container-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

Formula
1cqi = 1% of the query container's inline size
Relative to
Nearest query container ancestor (inline axis)
Support
Modern browsersall engines since Feb 2023

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.

Use when
  • 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 cqi over cqw for writing-mode safety
Avoid when
  • Without an ancestor container-type — the unit silently falls back to sv*
  • Page-level sizing where the viewport really is the reference — vw/vi say that directly
  • Inside an @container condition on the same element — a query can't test the container it lives on
Demo — Typography that scales with the container, not the viewport
Left container width240px

Aa

font-size: clamp(1rem, 6cqi, 2.5rem)

240px

Aa

same CSS, wider container

≈ fills remaining space

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

Formula
1cqb = 1% of the query container's block size
Relative to
Nearest query container ancestor (block axis)
Support
Modern browsersall engines since Feb 2023

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.

Use when
  • 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
Avoid when
  • Auto-height containers — with no definite block size these don't resolve meaningfully
  • Typical component work — cqi is usually the more practical starting point
  • Anywhere the container's height isn't something you control

cqmin / cqmax

Formula
1cqmin = 1% of the smaller container dimension; 1cqmax = 1% of the larger
Relative to
Nearest query container ancestor (both axes)
Support
Modern browsersall engines since Feb 2023

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 ·


Sources