This guide explains how CSS container queries allow components to adapt based on their parent container’s size rather than the viewport, making layouts more flexible and reusable with practical examples.
For years, web developers have relied on media queries to make layouts responsive. But what happens when a component needs to adapt based on where it’s placed, rather than the overall viewport size? That’s exactly the problem CSS container queries solve.
If you’ve been using media queries and component-based design but haven’t yet explored container queries, this guide will give you a solid foundation.
Media queries have been the go-to method for responsive design. They allow us to define styles based on the viewport size, which works well for full-page layouts.
For example, if you wanted a .card
component to stack vertically on smaller screens, you’d write:
@media (max-width: 600px) {
.card {
flex-direction: column;
}
}
The problem? This approach assumes that screen size is the only factor that matters. But what if that .card
appears inside a sidebar? Even if the viewport is large, the sidebar might be narrow, and the card should still stack.
Media queries don’t help here because they don’t account for the container’s size — only the entire page’s width. This is where container queries come in.
Container queries let components adjust their styles based on their parent container’s size, rather than the viewport.
To use container queries, you first need to declare a container. You do this with container-type
.
.card-container {
container-type: inline-size;
}
This tells the browser that .card-container
should act as a container, and its child elements can respond to its inline size (width).
You can also name a container for more specific targeting:
.card-container {
container-name: card;
container-type: inline-size;
}
The name is optional but useful if you have multiple container queries in a project.
container-type: size
– When to Use It #By default, container-type: inline-size
makes elements respond only to width. But what if you want styles to adapt based on both width and height? That’s where container-type: size
comes in.
.widget-container {
container-type: size;
}
size
Instead of inline-size
size
ensures the component adapts properly.Note:
container-type: size
does not enforce layout containment; it only allows queries to respond to both width and height.
.chat-widget {
container-type: size;
width: 300px;
min-height: 200px;
max-height: 600px;
overflow: auto;
border: 1px solid #ddd;
padding: 1rem;
}
/* Adjust styles when the chat widget is short */
@container (max-height: 250px) {
.chat-widget {
font-size: 14px;
padding: 0.5rem;
}
}
This ensures that when the chat widget shrinks, the font size and padding adjust accordingly.
Let’s put it all together with a practical example.
<div class="card-container">
<div class="card">
<img src="image.jpg" alt="Example">
<div class="content">
<h2>Card Title</h2>
<p>Card description goes here.</p>
</div>
</div>
</div>
.card-container {
container-type: inline-size;
}
.card {
display: flex;
gap: 1rem;
}
@container (max-width: 400px) {
.card {
flex-direction: column;
}
}
Now, if you drop .card-container
into a wide section, the .card
displays in a row. If you place it inside a sidebar, it might shrink below 400px and stack vertically—all without needing a viewport-based media query.
Let’s take another example: a sidebar navigation that collapses into an icon-only menu when space is limited.
<nav class="sidebar">
<ul>
<li><a href="#"><span class="icon">🏠</span> Home</a></li>
<li><a href="#"><span class="icon">📄</span> About</a></li>
<li><a href="#"><span class="icon">📞</span> Contact</a></li>
</ul>
</nav>
.sidebar {
container-type: inline-size;
width: 250px;
background: #f4f4f4;
padding: 1rem;
}
.sidebar ul {
list-style: none;
padding: 0;
}
.sidebar li {
margin-bottom: 1rem;
}
.sidebar a {
display: flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
color: black;
}
/* When the sidebar is narrow, hide text and show only icons */
@container (max-width: 150px) {
.sidebar a span {
display: none; /* Hide text */
}
}
inline-size
vs. size
#Property | What It Affects | Use Case |
---|---|---|
inline-size (default) | Width only | Most responsive components (cards, sidebars, grids) |
size | Both width and height | Dynamic-height elements (chat windows, widgets) |
If your component only changes in width, stick with inline-size
. If you need height-based adjustments as well, size
is the better option.
Container queries are a major step forward in responsive web design. If you’re already working with CSS media queries, they’re easy to learn — and once you start using them, you won’t want to go back.