Exploring Code #72: Container Queries in CSS
Container queries are awesome. Considering the always-increasing shift towards component-based development, creating a styling context that's attached to a container versus a viewport offers much more power and flexibility than the standard media query we are used to over-using.
Let's explore a little bit about what they are, the problems they solve, and when you should reach for them.
What Are Container Queries?
This image from this article does a good job at painting the picture.
Check out some of the pens on that page as well. Crazy cool to see those examples.
Essentially, a container query is like having multiple, viewport-agnostic sections of a page that are able to resize and reorient according to their own rules.
The Problems That Container Queries Solve
If you are just using media queries, I can bet you have to make a ton of minor tweaks to styles within those viewports on a page-by-page basis.
One min-width declaration at 768px won't be a 1:1 when comparing a collection page versus a product page versus a checkout page.
With container queries, you can style the actual container like a component and drop it on any page and it will resize responsively on a consistent basis if you set up the styles correctly.
That just makes so much more sense than having to tweak component containers on a page-by-page basis.
Syntax for Container Queries
First you need to set the container context on the parent element that is containing the element you will eventually style within the container query.
We use the container shorthand below where we are naming the context (card) and assigning the type (inline-size).
The inline-size CSS property defines the horizontal or vertical size of an element's block - source
.container {
container: card/inline-size; /* <container-name>/<container-type> */
border-radius: .5rem;
}
The HTML of a container that only contains one card (for brevity).
<section>
<div class="container">
<article>
<img src="https://dummyimage.com/600x600/004643/004643.jpg&text=pretty+picture"/>
<div class="text">
<h3>This is a Heading</h3>
<p>This is a sentence. Very interesting content here. Yep, this content is riveting. I wonder where this is going. I could've just used Lorem Ipsum, but that's boring and latin. No one actually likes Lorem Ipsum.</p>
</div>
</article>
</div>
</section>
And the actual container query looks just like a media query, but you can differentiate by name (card) and it even supports range syntaxes like below (inline-size > 350px).
IMPORTANT NOTE: a container query doesn't actually style the container itself. Just the items within the container.
So you can't self-referentially style the thing you are querying, just its items.
@container card (inline-size > 350px) {
/* ... */
}
Re-Tooling An Existing Codepen of Grid Cards
I was looking for concrete examples on Codepen, like I normally do, and this example by Asha Wrenn seemed like a good starting point for illustrating the concept.
The problem though, I noticed the container query styles weren't really working in the pen. Which is understandable given the many changes to the spec in recent months. So let's fix it and do some modifications.
So, this is what we have so far.
领英推荐
Just a standard grid layout based on this grid column declaration:
section {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 1rem 0;
}
For all available space, according to the amount of grid items, a card will never dip below a 200px minimum, and at maximum it will grow to fit the available space (1fr) evenly between grid items.
The auto-fit keyword in this context means that implicit rows are collapsed and explicit grid items are expanded.
In other words, if we used auto-fill instead of auto-fit, there would be a whole bunch of empty implicit rows that take up available space in the grid because of the repeat function.
That is, when the viewport has space for more than 3 explicit 200 pixel grid items, it will implicitly spit out as many additional 200px grid items it can fit within the bounds.
Versus auto-fit, where the potential implicit rows are collapsed to expand the explicit items:
CSS Grid Aside Over
With that out of the way, let's dive into the container query aspect where we will change the styles of the cards based on the container context inline-size (the inline width of each item).
Let's just change the layout based on an inline-size less than 350px for demo purposes.
Remember, using a container query means we are only affecting the child items within container itself.
Let's see what happens when we do something to top section first. We could call it the hero section realistically.
So, once it gets pretty big. We will change the styling of the single card completely. And replace the background image with a Capybara (why not).
(This is not exact at all btw. Just playing around with the numbers).
@container card (inline-size > 1200px) {
article {
display: flex;
padding: 1rem;
align-items: center;
justify-content: center;
background-image: url("https://images.unsplash.com/photo-1716337757705-c5f314ef84ee?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTZ8fGNhcHliYXJhfGVufDB8fDB8fHww");
border: none;
color: #fff;
background-repeat: no-repeat;
background-size: cover;
background-position: center;
}
img {
display: none;
}
.text {
max-width: 700px;
text-align: center;
}
h3 {
font-size: 4rem;
}
p {
font-size: 2rem;
}
}
And then next, let's say we wanted to change the layout of the cards to have the image aligned to the left once it hits a certain range.
This should only affect the two card layout given the inline-size dimension range stated below:
@container card (inline-size > 500px) and (inline-size <= 700px) {
article {
display: flex;
align-items: center;
flex-flow: nowrap;
}
article > img.card-image {
border-radius: 2rem;
height: 100%;
width: calc(50% - 4px);
}
div.text {
width: calc(50% - 4px);
}
}
Sure, not the prettiest, but pretty cool to start playing around with and getting better at designing layouts this way.