All articles
css

TIL: cqw scales with the card, not the viewport

I spotted 1.5cqw in a font shorthand inside opentunnel's stylesheet and had to look it up. Container query units are vw and vh for a component. 1cqw is 1% of the nearest container width, so a card can size itself to its own slot instead of the page.

Vinayak Kulkarni
2 min read

I spent some time poking at opentunnel.xyz today, after seeing dax post about it. Public URLs for anything running on your machine, end-to-end encrypted so the relay cannot read the traffic, plus an SDK to embed the whole thing. Good product, and I like reading how people build things.

Then one line in its stylesheet stopped me:

font:
  400 1.5cqw/1.75 var(--font-text),
  monospace;

I did not recognize 1.5cqw, so I went looking and found the csstools.io guide to container queries, which lays the whole thing out.

What cqw is

Container query units work like viewport units, except they measure the nearest container instead of the viewport.

UnitMeasures
cqw1% of the container width
cqh1% of the container height
cqi1% of the container inline size
cqb1% of the container block size
cqminThe smaller of cqi and cqb
cqmaxThe larger of cqi and cqb

The setup takes two steps. The parent declares itself a container, and the children query it:

.card-wrapper {
  container-type: inline-size;
}

.card-title {
  font-size: clamp(1rem, 4cqw, 2rem);
}

@container (min-width: 420px) {
  .card {
    flex-direction: row;
  }
}

One rule cost me a minute. An element cannot query its own size, only an ancestor's. That is why the pattern always needs a wrapper. The parent gets container-type, the child gets @container, and they can never be the same element.

In practice

vw ties a component to the page. cqw ties it to its slot. The same card in a narrow sidebar and a wide main column can scale its own type and padding from a single rule, with no breakpoint per location.

That is what opentunnel does with it. Their label text uses 1.5cqw so it shrinks with its container, a decorative element is 54cqw wide so it always spans the same fraction, and the responsive rules use the range syntax (@container (width<=560px)) rather than max-width.

Browser support: Chrome 105+, Safari 16+, Firefox 110+, and around 90% globally as of mid 2026. For a fallback, write a plain font-size first, then override it with the cqw version. Old browsers keep the static value.

Next time I set a font-size or padding inside a component that appears at more than one width, I am reaching for cqw before a media query.

csscontainer-queriescqwfrontend