Skip to content
Back to Design Hub

CSS Container Queries

Component-driven responsiveness: adapt to the parent container, not the viewport.

Responsive DesignIntermediate

Why

Media queries adapt to the viewport, so a card laid out in a sidebar and a full-width page needs two CSS rules. Container queries let the component respond to its own container — write once, drop anywhere.

StatCounter 2024 data shows 58% of web traffic is mobile. CSS container queries (baseline 2023) and subgrid (baseline 2024) eliminate most media-query hacks, reducing responsive CSS by 30-50%.

When

Use container queries for reusable components that live in different-width containers (card in sidebar vs main, nav in rail vs topbar). Baseline 2023, all evergreen browsers support them.

How

1. Declare a container — container-type: inline-size on the parent (size containment for width) 2. Query it — @container (min-width: 400px) { .card { ... } } adapts the child 3. Name containers — container-name: sidebar; query with @container sidebar (min-width: 300px) 4. Component-first — write the card's responsive rules against its container, never the viewport 5. Fallback — feature query @supports (container-type: inline-size) { ... } + a media-query fallback 6. Units — cqw/cqh (1cqw = 1% of container width), cqi/cqb for inline/block, cqmin/cqmax 7. Nesting — containers nest; the nearest ancestor container is queried, scope is automatic

Example

/* Card adapts to its container, not the viewport */ .card-host { container-type: inline-size; } .card { display: grid; grid-template-columns: 1fr; gap: 0.5rem; } @container (min-width: 400px) { .card { grid-template-columns: 120px 1fr; } /* image + text side-by-side */ } @container (min-width: 700px) { .card { grid-template-columns: 1fr; padding: 2rem; } /* hero variant */ } /* Named container — sidebar queries independently */ .sidebar { container-name: sidebar; container-type: inline-size; } @container sidebar (max-width: 200px) { .nav-item { flex-direction: column; } } /* Fallback for older browsers */ @supports not (container-type: inline-size) { @media (min-width: 768px) { .card { grid-template-columns: 120px 1fr; } } }

Common Problems

  • ⚠Forgetting container-type — query matches nothing because no ancestor is a container
  • ⚠Size containment side effects — container-type: size blocks the parent from sizing to content, layout breaks
  • ⚠Mixing viewport and container units — component looks right in one placement, wrong in another

Best Practices

  • ✓Use container-type: inline-size (width only) — size containment breaks height-to-content sizing
  • ✓Name containers when several coexist — avoids querying the wrong ancestor
  • ✓Provide a media-query fallback inside @supports not — older browsers still get a usable layout

FAQ