Expert8 min read

CSS Container Queries: Responsive Components Based on Parent Size

8 min read
302 words
15 sections11 code blocks

What Are Container Queries?

Container queries let you style an element based on the size of its parent container, not the viewport. This is a game-changer for component-based design because a component can adapt its layout based on the space it actually has.

The Problem Container Queries Solve

With media queries, a card component always responds to the viewport width:

CSS
/* Media query: based on viewport */
@media (min-width: 768px) {
  .card { flex-direction: row; }
}

But what if the same card is used in a wide main content area AND a narrow sidebar? With media queries, both cards get the same layout because the viewport is the same. Container queries fix this.

Setting Up a Container

First, define an element as a containment context:

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

container-type Values

CSS
/* Query based on inline (width) size */
container-type: inline-size;

/* Query based on both width and height */
container-type: size;

/* Normal element, no containment (default) */
container-type: normal;

inline-size is the most commonly used value, allowing you to query the container's width.

Writing Container Queries

Use @container instead of @media:

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

/* When the container is at least 400px wide */
@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }

  .card__image {
    width: 40%;
  }
}

/* When the container is at least 600px wide */
@container (min-width: 600px) {
  .card {
    gap: 2rem;
  }

  .card__title {
    font-size: 1.5rem;
  }
}

Naming Containers

Name your containers for clarity and to target specific containers:

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

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

/* Query a specific container by name */
@container sidebar (min-width: 300px) {
  .widget { padding: 1.5rem; }
}

@container main (min-width: 600px) {
  .article-card { flex-direction: row; }
}

container Shorthand

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

Practical Examples

Responsive Card Component

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

/* Default: small/stacked layout */
.card {
  display: flex;
  flex-direction: column;
  background: white;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.card__image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card__body {
  padding: 1rem;
}

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

  .card__image {
    width: 200px;
    height: auto;
  }

  .card__body {
    padding: 1.5rem;
  }
}

/* Large: enhanced horizontal layout */
@container (min-width: 600px) {
  .card__image {
    width: 280px;
  }

  .card__title {
    font-size: 1.5rem;
  }

  .card__body {
    padding: 2rem;
  }
}

Adaptive Navigation

CSS
.nav-container {
  container: nav / inline-size;
}

.nav-links {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

@container nav (min-width: 500px) {
  .nav-links {
    flex-direction: row;
    gap: 1rem;
  }
}

@container nav (min-width: 800px) {
  .nav-links {
    gap: 2rem;
  }

  .nav-link {
    font-size: 1.1rem;
  }
}

Dashboard Widget

CSS
.widget-wrapper {
  container-type: inline-size;
}

.widget {
  padding: 1rem;
}

.widget__stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.5rem;
}

@container (min-width: 300px) {
  .widget__stats {
    grid-template-columns: 1fr 1fr;
  }

  .widget {
    padding: 1.5rem;
  }
}

@container (min-width: 500px) {
  .widget__stats {
    grid-template-columns: repeat(4, 1fr);
  }
}

Container Query Units

CSS provides units relative to the container's size:

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

.card__title {
  /* cqi = 1% of container's inline size */
  font-size: clamp(1rem, 4cqi, 2rem);
}

Available Container Units

  • cqw: 1% of container's width
  • cqh: 1% of container's height
  • cqi: 1% of container's inline size
  • cqb: 1% of container's block size
  • cqmin: Smaller of cqi and cqb
  • cqmax: Larger of cqi and cqb

Container Queries vs Media Queries

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

/* Container query: responds to PARENT */
@container (min-width: 400px) {
  .card { flex-direction: row; }
}

Use media queries for page-level layout decisions (sidebar visibility, grid columns). Use container queries for component-level layout decisions (card orientation, widget density).

Summary

Container queries enable truly component-driven responsive design. Define a container with container-type: inline-size, write queries with @container, and name containers for targeting specific parents. Components now adapt to their available space rather than the viewport, making them reusable across different layout contexts. Combined with container query units, you can create fully fluid components that look perfect everywhere.