1. Container Query Basics

Set container-type: inline-size on a parent, then use @container rules to style children based on the container's width. Drag the resize handle to see it adapt.

Adaptive card (resize me)
Adaptive Card

This card switches from vertical to horizontal layout based on its container width, not the viewport.

Container Query
↔ Drag to resize

Stacks vertically when narrow, goes horizontal at 20rem+.

/* 1. Define a containment context */
.card-wrapper {
  container-type: inline-size;  /* track inline (width) size */
  container-name: card;         /* optional: name for targeting */
}

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

/* 2. Write container query rules */
@container card (min-width: 20rem) {
  .card { flex-direction: row; }
  .card__image { width: 40%; }
}

@container card (min-width: 32rem) {
  .card__title { font-size: 1.25rem; }
}

/* container-type values:
   inline-size  - queries based on inline (width) dimension
   size         - queries based on both width and height
   normal       - no containment (default) */

2. Container Queries vs Media Queries

Media queries respond to the viewport size. Container queries respond to the component's container. This means the same component adapts differently depending on where it's placed.

Side-by-side comparison
@media (viewport)
I respond to the browser window width. Resize the whole browser to see me change.
@container (parent)
I respond to my parent's width. Same component works in a sidebar, modal, or full-width section.

@media = viewport-based. @container = component-based. Container queries make components truly portable.

/* Media query: responds to VIEWPORT */
@media (min-width: 768px) {
  .card { flex-direction: row; }
}

/* Container query: responds to CONTAINER */
.sidebar { container-type: inline-size; }

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

/* Same .card component, different contexts:
   - In a narrow sidebar → stays vertical
   - In a wide main area → goes horizontal
   No extra classes needed! */

3. Named Containers

Use container-name to target specific containers when they are nested. Without a name, @container queries the nearest ancestor container.

Nested named containers (resize me)
container-name: outer
container-name: inner
This child queries both outer and inner containers separately. Resize to see different border colors appear.
↔ Drag to resize

Purple border = outer container is wide enough. Green background = inner container is wide enough.

/* Name containers to target them specifically */
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

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

/* Query a specific container by name */
@container sidebar (min-width: 15rem) {
  .widget { font-size: 0.875rem; }
}

@container main (min-width: 40rem) {
  .widget { display: grid; grid-template-columns: 1fr 1fr; }
}

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

4. Container Query Units

Container query units size things relative to the query container's dimensions, like vw/vh but for containers instead of the viewport.

cqi-based widths (resize me)
25cqi
50cqi
75cqi
100cqi
↔ Drag to resize

Each bar uses cqi (container query inline) units. They scale proportionally to the container width.

/* Container query units */
.parent { container-type: inline-size; }

.child {
  /* cqw  = 1% of container width */
  /* cqh  = 1% of container height */
  /* cqi  = 1% of container inline size */
  /* cqb  = 1% of container block size */
  /* cqmin = smaller of cqi/cqb */
  /* cqmax = larger of cqi/cqb */

  font-size: 5cqi;      /* scales with container width */
  padding: 2cqi 4cqi;   /* proportional padding */
  width: 50cqi;         /* always 50% of container */
}

/* Responsive typography that scales with container */
.heading {
  font-size: clamp(1rem, 5cqi, 3rem);
}

A feature list component that adapts its layout based on container size. List view when narrow, card grid when wide. Drag to resize.

Adaptive feature grid (resize me)
↔ Drag to resize

List → 2-column → 3-column as the container grows. Descriptions appear at 22rem+.

/* Container setup */
.features-wrapper {
  container: features / inline-size;
}

/* Default: stacked list */
.features-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}

/* Medium: 2-column cards */
@container features (min-width: 22rem) {
  .features-grid {
    grid-template-columns: repeat(2, 1fr);
  }
  .feature-card {
    flex-direction: column;
    text-align: center;
  }
  .feature-desc { display: block; }
}

/* Large: 3-column cards */
@container features (min-width: 36rem) {
  .features-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* Browser support: all modern browsers (Chrome 105+, Firefox 110+, Safari 16+) */