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 */
}
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;
}
}
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; }
}
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 */
}
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;
}
}
Navigation
.nav-container {
container-type: inline-size;
}
nav { display: flex; gap: 8px; }
@container (max-width: 600px) {
nav {
flex-direction: column;
align-items: stretch;
}
}
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); }
}
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; }
}
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 */
}
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; }
}
Top comments (0)