DEV Community

Weather Clock Dash
Weather Clock Dash

Posted on

CSS Container Queries: Truly Reusable Components That Adapt to Their Container

Container Queries are one of the most significant additions to CSS in years. Instead of styling components based on the viewport (media queries), you style them based on their container's size. This makes truly reusable components possible — a card component looks right whether it's in a narrow sidebar or a wide main column.

The Problem with Media Queries for Components

With media queries:

/* Works fine in the main column */
.card { font-size: 16px; }

@media (max-width: 768px) {
  .card { font-size: 14px; } /* Assumes small viewport = narrow card */
}
Enter fullscreen mode Exit fullscreen mode

But what if the card is in a sidebar on a wide viewport? The sidebar might be 300px wide even on a 1440px screen — the media query doesn't trigger, so the card looks wrong.

Container queries solve this: the card responds to its container's size, not the viewport.

Basic Container Query

/* Step 1: Define a containment context */
.sidebar {
  container-type: inline-size; /* Enable inline-size queries */
  container-name: sidebar;     /* Optional: name for targeting */
}

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

/* Step 2: Style children based on container size */
@container (min-width: 400px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

@container (max-width: 399px) {
  .card {
    display: block;
  }
}
Enter fullscreen mode Exit fullscreen mode

Named Containers

Target a specific ancestor:

.main-layout {
  container: main / inline-size;
}

.sidebar-layout {
  container: sidebar / inline-size;
}

/* Only applies when card is inside .sidebar-layout < 300px */
@container sidebar (max-width: 300px) {
  .card { padding: 8px; }
}

/* Only applies when card is inside .main-layout > 600px */
@container main (min-width: 600px) {
  .card { padding: 24px; }
}
Enter fullscreen mode Exit fullscreen mode

Container Query Units

Container queries introduce new units relative to the container:

.card {
  /* cqi: container query inline (horizontal) */
  font-size: max(14px, 2.5cqi); /* At least 14px, grows with container */

  /* cqw: container query width */
  width: 50cqw; /* 50% of container width */

  /* cqh: container query height */
  max-height: 80cqh; /* 80% of container height */

  /* cqb: container query block (vertical) */
  /* cqmin, cqmax: min/max of cqi/cqb */
}
Enter fullscreen mode Exit fullscreen mode

Practical Examples

Card Component

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

.card {
  padding: 16px;
}

.card-image {
  width: 100%;
  aspect-ratio: 16/9;
}

/* Wide container: horizontal layout */
@container (min-width: 480px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
    gap: 16px;
  }

  .card-image {
    width: 200px;
    aspect-ratio: 1;
  }
}
Enter fullscreen mode Exit fullscreen mode

Navigation

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

nav { display: flex; gap: 8px; }

@container (max-width: 600px) {
  nav {
    flex-direction: column;
    align-items: stretch;
  }
}
Enter fullscreen mode Exit fullscreen mode

Product Grid

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

.product-grid {
  display: grid;
  grid-template-columns: 1fr; /* Single column by default */
  gap: 16px;
}

@container (min-width: 400px) {
  .product-grid { grid-template-columns: repeat(2, 1fr); }
}

@container (min-width: 700px) {
  .product-grid { grid-template-columns: repeat(3, 1fr); }
}

@container (min-width: 1000px) {
  .product-grid { grid-template-columns: repeat(4, 1fr); }
}
Enter fullscreen mode Exit fullscreen mode

The grid adapts to its container, not the viewport. Put this product grid in a modal, a sidebar, or a full-width section — it always shows the right number of columns for the available space.

Container Query + CSS Grid Combination

.page {
  display: grid;
  grid-template-columns: minmax(200px, 300px) 1fr;
}

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

/* Sidebar cards are narrow — stack layout */
@container (max-width: 250px) {
  .card { flex-direction: column; }
}

/* Main cards are wider — side-by-side layout */
@container (min-width: 500px) {
  .card { flex-direction: row; }
}
Enter fullscreen mode Exit fullscreen mode

Browser Support

Firefox 110+ supports container queries. All modern browsers support them as of 2023.

Check support:

@supports (container-type: inline-size) {
  /* Use container queries */
}
Enter fullscreen mode Exit fullscreen mode

Container Queries vs. Media Queries

Container queries don't replace media queries:

  • Media queries: global layout decisions (sidebar visible at >= 768px)
  • Container queries: component-level decisions (card layout when container >= 400px)

Use both together:

/* Media query: sidebar visible/hidden */
@media (min-width: 768px) {
  .sidebar { display: block; }
}

/* Container query: card layout inside sidebar vs. main */
@container (min-width: 400px) {
  .card { flex-direction: row; }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)