CSS Container Queries: The Layout Primitive I've Been Waiting For

After years of media query gymnastics and ResizeObserver hacks, container queries finally let components style themselves based on the space available to them — not the viewport.

The media query was always a hack for component design. @media (max-width: 768px) tells you about the viewport, not about the space your component actually has. A card component placed in a 300px sidebar should render differently than the same card component placed in a full-width grid — but with media queries, they'd be styled identically if the viewport was the same width.

Container queries solve this properly. They've been in the Baseline "Newly Available" tier since 2023 and as of early 2026 have excellent browser support (95%+ global coverage). It's time to start using them.

Basic syntax

Container queries require two things: declaring a containment context on a parent element, then writing queries relative to that container on child elements.

/* Step 1: declare the container */
.card-wrapper {
  container-type: inline-size;
  container-name: card;  /* optional, but useful */
}

/* Step 2: query the container */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 150px 1fr;
  }
}

@container card (max-width: 399px) {
  .card {
    display: block;
  }
}

container-type: inline-size is the key declaration. It enables size containment on the inline axis (usually width), which is what allows container queries to work. The shorthand container property lets you set both at once:

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

A real example: responsive card

Here's a pattern I've used across three projects: a content card that switches between stacked and side-by-side layout based on available width, independent of the viewport.

.card-container {
  container: content-card / inline-size;
}

.card {
  background: var(--surface);
  border-radius: 8px;
  overflow: hidden;
}

/* Stacked (default / narrow) */
.card-image {
  aspect-ratio: 16 / 9;
  width: 100%;
  object-fit: cover;
}

.card-body {
  padding: 16px;
}

.card-title { font-size: 1rem; }
.card-excerpt { display: none; }

/* Wide layout: side by side */
@container content-card (min-width: 480px) {
  .card {
    display: grid;
    grid-template-columns: 180px 1fr;
  }

  .card-image {
    aspect-ratio: auto;
    height: 100%;
  }

  .card-body {
    padding: 20px 24px;
  }

  .card-title   { font-size: 1.1rem; }
  .card-excerpt { display: block; }
}

/* Very wide: bigger type, more padding */
@container content-card (min-width: 700px) {
  .card-body    { padding: 28px 32px; }
  .card-title   { font-size: 1.3rem; }
  .card-image   { width: 240px; }
}

Now this card component works correctly whether it's placed in a narrow sidebar, a two-column grid, or a full-width feed — without touching the CSS when you move it between contexts.

Style queries

Less discussed but equally powerful: style queries let you branch on computed custom property values. This enables a new pattern for theming:

.card-container {
  container-type: style;
}

/* Apply dark variant when parent sets --theme: dark */
@container style(--theme: dark) {
  .card {
    background: #1a1a2e;
    color: #e8eef8;
    border-color: #2a3f60;
  }
}

/* Apply compact variant */
@container style(--density: compact) {
  .card-body { padding: 8px 12px; }
  .card-title { font-size: .9rem; }
}

This is a cleaner alternative to BEM modifiers and data attributes for variant-based styling.

Caveats and gotchas

A few things to watch out for:

You can't query a container from inside itself. A container cannot respond to its own size — only its descendants can. This is intentional (it prevents circular dependencies) but it means you need a wrapper element.

container-type: size vs inline-size. size enables queries on both axes and requires the element to have a definite height, which can cause issues with height-auto layouts. Stick with inline-size unless you specifically need height queries.

Style queries are still flagged in some browsers. Size queries have full support; style queries are still behind flags in some contexts as of mid-2026. Test before shipping.

Container queries don't replace media queries — you still need media queries for viewport-level layout decisions (like sidebar visibility or overall page grid). They're complementary: media queries for the macro layout, container queries for the component internals.