---
title: "CSS: Container Queries"
slug: "css-container-queries"
category: "frontend"
tags: ["frontend", "css", "container-queries", "responsive", "layout"]
status: "stable"
last_updated: 2026-05-14
summary: "@container for component-driven responsive layouts: container-type, container-name, cqi units, and the pattern that replaces viewport breakpoints."
related:
  [
    "[[frontend/css]]",
    "[[frontend/css-grid]]",
    "[[frontend/css-flexbox]]",
    "[[frontend/tailwind]]",
    "[[frontend/accessibility]]",
  ]
---

## Overview

Container queries let a component restyle itself based on its parent's size, not the window's. A card in a 240px sidebar and the same card in an 800px main column render with different layouts and no class-string conditionals. The umbrella sits at [[frontend/css]]; this page covers how to ship container-driven components in production.

## Query the container, not the viewport

Reach for `@container` when a component should look different based on the space it has, not based on the device. Reach for `@media` when the rule is genuinely viewport-driven (touch targets, print, reduced motion).

```css
.card {
  container-type: inline-size;
}

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

@container (min-width: 32rem) {
  .card {
    display: grid;
    grid-template-columns: 12rem 1fr;
  }
  .card .title {
    font-size: 1.25rem;
  }
}
```

The same `.card` markup adapts to its slot. A page that drops cards into a 3-column grid and into a sidebar with one stylesheet is the typical win.

## Set `container-type: inline-size` on the parent

Containment must be declared. Without `container-type`, `@container` rules match nothing.

- `inline-size`: container queries on width only. Cheap; the common choice.
- `size`: container queries on width and height. More expensive; require an explicit block size on the container.
- `normal`: not a container; use to opt out.

```css
.sidebar {
  container-type: inline-size;
}
.card {
  container-type: inline-size;
}
```

A page-level container around the main column is enough to drive nested components. Do not declare `container-type` on `html` or `body`; it shifts the containing block in ways that break sticky positioning and percentage heights.

## Name containers when nesting

Without a name, `@container` matches the nearest containing-ancestor that opted in. Name containers when you have nested ones and need to target a specific level.

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

@container card (min-width: 24rem) {
  .card .title {
    font-size: 1.25rem;
  }
}
@container cards (min-width: 60rem) {
  .cards {
    grid-template-columns: repeat(3, 1fr);
  }
}
```

Use the `container` shorthand to set both at once: `container: card / inline-size`.

## Use `cqi`, `cqw`, and friends for container-relative sizing

Container query units size relative to the container, the way `vw` and `vh` size relative to the viewport.

- `cqi`: 1% of the container's inline size (the common one).
- `cqb`: 1% of the container's block size.
- `cqw` and `cqh`: width and height variants.
- `cqmin` and `cqmax`: the smaller and larger of the two axes.

```css
.card .title {
  font-size: clamp(1rem, 4cqi, 1.5rem);
}
```

Fluid type inside a container scales with the container, not the viewport. This is what `clamp(... + vw, ...)` was approximating before container units shipped.

## Adopt the responsive component pattern

A reusable component owns its breakpoints and its container declaration. The page composes components; the component decides how to render.

```css
.media {
  container-type: inline-size;
  display: grid;
  gap: 1rem;
}
@container (min-width: 28rem) {
  .media {
    grid-template-columns: 8rem 1fr;
    align-items: start;
  }
}
```

The page that drops `.media` into a sidebar gets the stacked layout. The page that drops it into a wide column gets the row layout. Same markup, same class, no page-level conditionals. Pair with [[frontend/css-grid]] for the layout primitive and [[frontend/tailwind]] for the `@container` utility variants.

## Browser support is shipped; the polyfill is gone

Container queries reached every evergreen browser by mid-2023. As of 2026, no polyfill is needed. Style queries (`@container style(...)`) and scroll-state queries are progressing through implementations; check Baseline before relying on them. The size-query subset, the one this page covers, is safe to ship without fallbacks.

## Common pitfalls

- **Forgetting `container-type`.** `@container` rules silently match nothing. The component looks like the queries are broken; they have no container.
- **Querying the wrong ancestor.** Nested unnamed containers match the nearest. Name containers when nesting and the right rule applies.
- **`container-type: size` without a block size.** The container collapses or breaks layout. Use `inline-size` unless you genuinely need height queries.
- **Containment on `body`.** Breaks position: sticky and percentage heights site-wide. Move containment to the actual component wrapper.
- **Tab order changes on layout change.** Reordering items with `grid-template-areas` between two queries can desync DOM and visual order; see [[frontend/accessibility]].

## Related

- [[frontend/css]]
- [[frontend/css-grid]]
- [[frontend/css-flexbox]]
- [[frontend/tailwind]]
- [[frontend/accessibility]]
- [[seo/page-speed]]
