Skip to content

Container Queries

beginner10 min read

Components That Respond to Their Own Size

Think about this: media queries respond to the viewport. But a card component can live in a full-width main area or a narrow sidebar -- the viewport is the same, but the card's available space is completely different. Container queries let components respond to their container's size, not the viewport. This is the missing piece for truly reusable responsive components.

Mental Model

Think of container queries as thermometers on each room, not just one for the building. Media queries are like a single building thermostat — every room gets the same temperature adjustment. Container queries give each room its own thermostat. A card in the sidebar adjusts to sidebar width. The same card in the main content adjusts to main content width. The component is self-aware.

Setting Up Containment

A container query requires two things: a container context on the parent and a @container rule on the children.

/* Step 1: Declare a container */
.card-wrapper {
  container-type: inline-size; /* Enable width-based container queries */
}

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

@container (min-width: 600px) {
  .card {
    grid-template-columns: 300px 1fr;
    gap: 2rem;
  }
}

container-type Values

.a { container-type: inline-size; } /* Query width only (most common) */
.b { container-type: size; }        /* Query both width and height */
.c { container-type: normal; }      /* Default — no containment */
Common Trap

container-type: inline-size establishes size containment on the inline axis. This means the container's width can no longer depend on its children's widths — it breaks the intrinsic sizing feedback loop. If you set container-type: inline-size on an element without explicit width, it may collapse to zero width if it would normally be sized by content.

Named Containers

When containers are nested, you need names to target the right one:

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

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

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

/* Query a specific container by name */
@container sidebar (min-width: 300px) {
  .widget { display: grid; grid-template-columns: 1fr 1fr; }
}

@container main-content (min-width: 800px) {
  .article { columns: 2; }
}

Without a name, @container queries the nearest ancestor with container-type set.

Container Query Units

It gets better. Container queries also provide new units relative to the container's size:

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

.card-title {
  font-size: clamp(1rem, 3cqi, 1.5rem); /* cqi = 1% of container inline size */
}

.card-image {
  height: 30cqb; /* cqb = 1% of container block size */
  /* Requires container-type: size (not just inline-size) */
}
UnitMeaning
cqw1% of container width
cqh1% of container height
cqi1% of container inline size
cqb1% of container block size
cqminSmaller of cqi/cqb
cqmaxLarger of cqi/cqb

Production Scenario: Reusable Card Component

/* The card container — wherever it lives */
.card-container {
  container: card / inline-size;
}

/* Default: vertical stack (narrow context — sidebar, mobile) */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.card-image {
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  overflow: hidden;
}

.card-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Medium: horizontal layout */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
    align-items: flex-start;
  }

  .card-image {
    flex: 0 0 180px;
    aspect-ratio: 1;
  }
}

/* Large: rich layout with metadata row */
@container card (min-width: 600px) {
  .card-image {
    flex: 0 0 240px;
  }

  .card-meta {
    display: flex;
    gap: 1rem;
    margin-top: auto;
  }
}

This card works in a sidebar (stacks vertically), in a two-column grid (horizontal), and in a full-width hero area (rich layout) -- all without a single media query. The component is self-aware. Drop it anywhere and it just works.

Container Queries vs Media Queries

So when do you use which? Here's the breakdown.

FeatureMedia QueriesContainer Queries
Responds toViewport sizeContainer size
Best forPage-level layout changesComponent-level responsiveness
ReusabilityComponent behavior tied to viewportComponent works anywhere
NestingN/AQuery specific named containers
Browser supportUniversalModern browsers (2023+)

Decision framework:

  • Page layout (sidebar visibility, nav style): Use media queries
  • Component layout (card orientation, widget density): Use container queries
  • Sizing (font-size, padding): Use fluid values (clamp) first, container queries for step changes
Execution Trace
Containment
container-type: inline-size on .card-wrapper
Width containment established
Layout
Browser computes .card-wrapper width: 350px
Based on parent layout, NOT card content
Evaluate
@container card (min-width: 400px) → false
350px < 400px — this query doesn't match
Fallback
Card renders in vertical stack (default styles)
No container query styles applied
Resize
Parent expands — .card-wrapper becomes 500px
@container (min-width: 400px) → true, horizontal layout activates
What developers doWhat they should do
Setting container-type on the component itself instead of its wrapper
The container must be the element whose size the component should respond to. A component can't query its own size.
Set container-type on the PARENT that provides the available space, not the component being styled
Using container-type: size when only width queries are needed
container-type: size also constrains height, which can cause elements to collapse if height isn't explicitly set
Use container-type: inline-size unless you specifically need height containment
Using container queries for page-level layout changes
Container queries are for component reusability. Page-level layout doesn't need to respond to container size.
Use media queries for page-level layout (sidebar, nav). Use container queries for component-level responsiveness.
Forgetting that containment prevents intrinsic sizing from content
container-type: inline-size means the container ignores its content's width — it must get width from outside
Ensure containers have width from their layout context (grid, flex, explicit width), not from content
Quiz
Where do you set container-type to make a card respond to its available space?
Quiz
What does container-type: inline-size do to the element's sizing behavior?
Key Rules
  1. 1Set container-type on the parent that provides space, not on the component being queried
  2. 2Use container-type: inline-size for width queries (most common), size for width + height
  3. 3Name containers when nesting to target the correct ancestor
  4. 4Container queries are for component-level responsiveness. Media queries are for page-level layout.
  5. 5Container units (cqi, cqb) let you size things relative to the container, not the viewport