CSS Container Queries: Responsive Components Based on Parent Size
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:
/* 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:
.card-wrapper {
container-type: inline-size;
}container-type Values
/* 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:
.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:
.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
/* container: name / type */
.sidebar {
container: sidebar / inline-size;
}Practical Examples
Responsive Card Component
.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
.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
.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:
.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
/* 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.