Episode 1 — Fundamentals / 1.8 — CSS Layout Mastery

1.8.g — Container Queries

In one sentence: Container queries let a component adapt its styles based on its container's size instead of the viewport — making truly reusable, context-aware components possible in pure CSS.

Navigation: ← 1.8.f — Stacking Context · 1.8 Overview


1. The problem container queries solve

Media queries respond to the viewport width:

@media (max-width: 600px) {
  .card { flex-direction: column; }
}

But a card component does not care about the viewport — it cares about how much space it actually has. A card in a narrow sidebar should look different from the same card in a wide main area, regardless of viewport width.

Viewport: 1200px

┌──────────┬──────────────────────────────────┐
│ Sidebar  │         Main content             │
│ (300px)  │         (900px)                  │
│          │                                  │
│ ┌──────┐ │  ┌─────────────────────────────┐ │
│ │ Card │ │  │           Card              │ │
│ │ (col)│ │  │         (row layout)        │ │
│ └──────┘ │  └─────────────────────────────┘ │
└──────────┴──────────────────────────────────┘

Same component, same viewport — different layout based on CONTAINER width.

2. @container syntax

Step 1: Define a container

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

Step 2: Query the container

@container sidebar (max-width: 400px) {
  .card {
    flex-direction: column;
  }
}

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

If you omit container-name, the query matches the nearest ancestor with container-type set.

@container (min-width: 500px) {
  .card { /* matches nearest container ancestor */ }
}

3. Container types

container-typeWhat it enablesContainment
inline-sizeQueries on width (inline dimension)Size containment on inline axis
sizeQueries on width and heightSize containment on both axes
normal (default)No container queriesNo containment

inline-size is the most common — you almost always want to query width, not height.

container shorthand

.panel {
  container: panel / inline-size;
  /* equivalent to: container-name: panel; container-type: inline-size; */
}

4. Container query units

Container queries come with relative units based on the container's dimensions:

UnitRelative to
cqw1% of container's width
cqh1% of container's height
cqi1% of container's inline size
cqb1% of container's block size
cqminSmaller of cqi and cqb
cqmaxLarger of cqi and cqb
@container (min-width: 400px) {
  .card-title {
    font-size: clamp(1rem, 3cqi, 1.5rem);
  }
}

These units let typography and spacing scale with the component's container, not the viewport.


5. Responsive components vs responsive pages

ApproachResponds toBest for
Media queries (@media)ViewportPage-level layout, global breakpoints
Container queries (@container)ContainerReusable components, design system widgets

The two are complementary:

  • Use media queries for the page skeleton (grid columns, mobile nav toggle).
  • Use container queries for components that appear in multiple contexts (cards, widgets, data tables).

6. Browser support

Container queries are supported in all modern browsers (Chrome 105+, Firefox 110+, Safari 16+). For production use, this covers the vast majority of users as of 2025/2026.

Fallback strategy: Write the mobile/small layout as the default, and enhance with @container for wider containers. Browsers that do not support container queries simply see the default layout.

/* Default: stacked layout */
.card {
  display: flex;
  flex-direction: column;
}

/* Enhanced: row layout when container is wide enough */
@container (min-width: 500px) {
  .card {
    flex-direction: row;
  }
}

7. Practical examples

Card that adapts to its container width

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

.card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.card img {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}

@container (min-width: 500px) {
  .card {
    flex-direction: row;
  }
  .card img {
    width: 200px;
    aspect-ratio: 1;
  }
}

@container (min-width: 800px) {
  .card img {
    width: 300px;
  }
  .card h2 {
    font-size: 1.5rem;
  }
}

Sidebar widget that simplifies at small size

.widget-container {
  container: widget / inline-size;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

@container widget (max-width: 300px) {
  .stats-grid {
    grid-template-columns: 1fr; /* stack when narrow */
  }
  .stat-label {
    display: none; /* show only the number in tight spaces */
  }
}

Responsive navigation within a component

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

.nav-links {
  display: flex;
  gap: 16px;
}

@container (max-width: 400px) {
  .nav-links {
    flex-direction: column;
    gap: 8px;
  }
}

8. Key takeaways

  1. Container queries make components responsive to their container, not the viewport.
  2. Set container-type: inline-size on the container, then query with @container.
  3. Use cqi/cqw units for container-relative sizing.
  4. Media queries handle page-level layout; container queries handle component-level responsiveness.
  5. Browser support is solid — write mobile-first defaults and enhance with @container.

Explain-It Challenge

Explain without notes:

  1. Why can't media queries solve the problem of a card that appears in both a sidebar and a main content area?
  2. What is the difference between container-type: inline-size and container-type: size?
  3. How would you provide a fallback for browsers that do not support container queries?

Navigation: ← 1.8.f — Stacking Context · 1.8 Overview