Container Query Units Explained: cqi, cqb, cqw, cqh, cqmin and cqmax

Problem statement

You have a card that lives in a sidebar on one screen and spans the full content column on another, and you want its padding, heading size, and gap to scale with the space the card actually occupies rather than with the viewport. Viewport units (vw, vh) only know about the screen, so a card in a 280px sidebar gets the same 4vw padding as a card in a 1200px hero. Container query length units solve exactly this: they resolve against the measured size of the nearest query container, letting one component scale correctly in every slot. This guide sits under Container Query Syntax Basics, part of Mastering Container Queries & Responsive Layouts, and focuses narrowly on the six length units and how each resolves.

Approach rationale

The six units — cqw, cqh, cqi, cqb, cqmin, cqmax — each represent 1% of a dimension of the query container. cqw and cqh are the physical width and height. cqi and cqb are the logical inline and block sizes, which track the writing mode rather than the screen axis. cqmin resolves to the smaller of cqi/cqb, and cqmax to the larger. Because these units read the container box directly, you get continuous fluid scaling with no breakpoints and no JavaScript ResizeObserver callback. That matters for accessibility too: a ResizeObserver-driven resize fires layout work on the main thread and can lag behind the paint, producing visible jumps for users on low-powered devices, whereas container units are resolved by the engine during layout in a single pass.

The one caveat is unbounded scaling. A raw font-size: 5cqi will keep growing as the container widens, eventually overflowing the line. The fix is to compose the unit inside clamp() so it has a floor and a ceiling — which is also how you combine it cleanly with the techniques covered in the guide to fluid typography with clamp().

Complete working implementation

Container query length axes A container box showing the inline axis resolving cqi and the block axis resolving cqb, with cqmin and cqmax derived from them. How cqi and cqb map to a query container query container container-type: inline-size inline = 100cqi block = 100cqb cqmin = min cqmax = max

The component below is self-contained: drop it into an empty HTML file and resize the browser. Both cards use identical CSS, yet each scales to the column it lands in.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
  .layout {
    display: grid;
    grid-template-columns: 1fr 3fr; /* sidebar, then main */
    gap: 2rem;
    padding: 2rem;
  }

  /* Each slot becomes a query container so cq* units read ITS size, not the viewport */
  .slot {
    container-type: inline-size;
  }

  .card {
    /* Padding scales with the container's inline size, clamped to sane bounds */
    padding: clamp(0.75rem, 4cqi, 2rem);
    /* Gap also tracks inline size, but never collapses below 0.5rem */
    display: grid;
    gap: clamp(0.5rem, 2cqi, 1.25rem);
    border-radius: clamp(8px, 2cqi, 18px);
    background: #11141c;
    color: #e8ecf4;
  }

  .card h2 {
    /* Fluid heading: floor 1.1rem, scales at 6% of inline size, ceiling 2rem */
    font-size: clamp(1.1rem, 6cqi, 2rem);
    line-height: 1.15;
    margin: 0;
  }

  .card p {
    /* cqmin keeps body text sensible even in a tall, narrow container */
    font-size: clamp(0.9rem, 1.6cqmin + 0.6rem, 1.05rem);
    margin: 0;
    opacity: 0.85;
  }

  /* cqb (block axis) used for a top accent bar that grows with container height */
  .card::before {
    content: "";
    display: block;
    height: clamp(3px, 1cqb, 10px);
    background: #7aa2ff;
    border-radius: 999px;
  }
</style>
</head>
<body>
  <div class="layout">
    <div class="slot">
      <article class="card">
        <h2>Sidebar card</h2>
        <p>Narrow container: cqi is small, so padding and heading stay compact.</p>
      </article>
    </div>
    <div class="slot">
      <article class="card">
        <h2>Main column card</h2>
        <p>Wide container: the same cqi rules produce larger type, gaps and padding.</p>
      </article>
    </div>
  </div>
</body>
</html>

Key technique callout

The unit that does the real work here is cqi. It is defined as 1% of the query container's inline size — the dimension along which text flows. In the default left-to-right, horizontal writing mode that is the container's width, so 6cqi is 6% of the card's rendered width. Because both cards share the same CSS but live in differently sized .slot containers, the engine resolves 6cqi to a different pixel value for each, with no media query and no script. Wrapping every cqi value in clamp(min, preferred, max) is what makes this production-safe: the middle argument supplies the fluid behavior while the floor and ceiling guarantee the component never produces unreadably small or comically large text. Note that container-type: inline-size only establishes the inline axis as a query dimension; cqb still resolves against the element's natural block size, which is why the accent bar above scales gently rather than dramatically.

Variation or extension

Under a vertical writing mode the physical and logical units diverge, and this is precisely why cqi is the safer default. Add the rule below and the inline axis becomes vertical, so cqi now tracks height while cqw would still track width.

/* RTL / vertical writing: cqi follows the inline (now vertical) axis automatically */
.slot {
  container-type: inline-size;
}

.vertical .slot {
  writing-mode: vertical-rl;
}

/* This heading scales correctly in BOTH writing modes because it uses cqi, not cqw */
.card h2 {
  font-size: clamp(1.1rem, 6cqi, 2rem);
}

If you had written 6cqw, the heading would scale with physical width even after the text rotated to flow vertically, breaking the relationship between type size and the line it sits on. Using the logical cqi/cqb pair keeps fluid sizing correct for internationalized layouts without writing a second rule.

Browser support note

Container query length units shipped alongside size container queries and are part of the same baseline: Chrome and Edge 105+, Safari 16.0+, and Firefox 110+ all resolve cqw, cqh, cqi, cqb, cqmin, and cqmax. There is no separate flag for the units once size queries are supported. For older engines the units degrade to their viewport equivalents only when no ancestor is a query container; in practice you should still ship a static fallback value first so unsupported browsers render a fixed size, as covered in the guide to feature detection with @supports.

FAQ

What is the difference between cqw and cqi?cqw is always 1% of the container's physical width, while cqi is 1% of its inline size. In a standard horizontal writing mode they resolve to the same value, but cqi follows the writing direction, so it maps to height under vertical writing modes. Prefer cqi for logical, direction-agnostic layouts.

Why does my cqi value resolve to zero? Container query units only resolve against an ancestor that declares container-type. If no ancestor is a query container, the unit falls back to the small-viewport equivalent, and if the queried axis has no definite size the unit computes to zero. Set container-type: inline-size on the parent.

Can I use cqi for font sizing directly? Yes, but wrap it in clamp() so the text stays within a readable minimum and maximum. A bare cqi font size scales without limit and becomes unreadable in very wide or very narrow containers.

Do container query units work without an @container rule? Yes. The length units resolve from the container's measured size independently of any @container block, so you can use cqi for fluid sizing even when you write no conditional queries at all.

Related articles

More pages in the same section.