⚡ AMP
CSS

CSS container queries: truly responsive components

A practical guide to cSS container queries: truly responsive components.

Nitheesh DR 4 min read

CSS Container Queries: Truly Responsive Components

You build a card component that looks great in a two-column grid. Someone reuses it in a narrow sidebar, and the text overflows because your media query only checks the viewport width — which is still "desktop-sized" even though the component itself is squeezed into 240px. Media queries answer "how wide is the screen," not "how wide is this component," and that mismatch is exactly what container queries fix.

The core idea

Media queries respond to the viewport. Container queries respond to a specific container's size, regardless of where that container sits on the page:

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

.card {
  display: flex;
  flex-direction: column;
}

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

Now .card switches to a horizontal layout whenever its container is at least 400px wide — whether that's a full-width hero section or a 400px-wide sidebar panel, it responds the same way, correctly, in both places.

Setting up a container

Any element can become a query container by giving it container-type:

.sidebar {
  container-type: inline-size;
}

inline-size queries just the width (the most common case). size queries both width and height, but requires the container to have an explicit size — it won't work on an element that sizes itself based on its content, since that would create a circular dependency.

A practical example: a reusable stat widget

.stat-widget {
  container-type: inline-size;
  container-name: stat;
}

.stat-widget .value {
  font-size: 1.5rem;
}

@container stat (min-width: 300px) {
  .stat-widget {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 1rem;
  }
  .stat-widget .value {
    font-size: 2.5rem;
  }
}

Drop this widget into a narrow dashboard tile and it stacks vertically with a small number. Drop the exact same component into a wide feature panel and it switches to a horizontal layout with a bigger number — no JavaScript, no separate component variant, no props threading "isCompact" down from a parent that has to know about viewport breakpoints.

Container query units

cqw, cqh, cqi, cqb scale relative to the container instead of the viewport:

@container stat (min-width: 300px) {
  .stat-widget .value {
    font-size: clamp(1.5rem, 8cqi, 3rem);
  }
}

8cqi means "8% of the container's inline size" — the text scales smoothly with the container's actual width rather than jumping between fixed sizes at breakpoints.

Common mistakes

What I'd actually use

Container queries for any component meant to be reused in genuinely different layout contexts — cards, widgets, sidebar panels — and media queries for the outer page-level layout (overall grid, nav collapse points). They're complementary, not competing. Browser support is solid across all current major browsers now, so there's little reason to avoid them for new component work.

Next steps

Pick one component in your codebase that looks wrong when reused in a narrower context than it was designed for, and try wrapping its parent in container-type: inline-size with a single @container breakpoint. It's usually a smaller change than the JavaScript-based "isCompact" prop it replaces.