HTML/CSS - 27
HTML/CSS - 27: Container Queries & Modern Selectors
Responsive design does not always have to depend on the size of the whole browser window. Sometimes a component should adapt to the space available inside its own container.
CSS Container Queries let you do exactly that. Instead of asking, "How wide is the screen?", you can ask, "How wide is this component's container?"
You will also experiment with modern selectors such as :has(), :is(), and :where(), which can make CSS more expressive and reduce repetitive selectors.
Your challenge: Create a reusable card that changes its layout when its container becomes wider, then use modern selectors to style different parts of the card.
How are container queries different from media queries?
A media query usually responds to the size of the browser viewport. A container query responds to the size of a particular container.
That means the same card component can behave differently depending on where it is placed. A card inside a narrow sidebar could stay compact, while the same card inside a wide content area could use a larger layout.
Step 1: Create the container
The rule: container-type: inline-size; tells the browser that the width of .card-container can be used for container queries.
The .card inside it is the element that will respond to changes in that container.
Step 2: Add a container query
Try: @container (min-width: 450px)
Inside it, change the card's padding or font size. For example: .card { padding: 35px; }
When the container is at least 450 pixels wide, the new rule can take effect.
Experiment: Change the container's max-width between 350px, 500px, and 700px. Watch when the container-query styles begin to apply.
This is different from resizing the entire browser because the component is reacting to its own available space.
What does :is() do?
The selector: .card :is(h2, h3) matches both <h2> and <h3> elements inside a card.
Without :is(), you might write: .card h2, .card h3.
The result can be the same, but :is() can make longer selector groups easier to read.
What about :where()?
:where() also lets you group selectors. For example: .card :where(p, ul, ol) could target paragraphs and lists inside a card.
One useful difference is that :where() is designed to be easy to override later, which can be helpful when building reusable styles.
Now try :has():
The :has() selector lets a parent react to something it contains.
For example: .card:has(button) selects a card only when that card contains a button.
Try: .card:has(button) { border-style: solid; }
Then remove the button from the HTML temporarily. The selector should stop matching the card.
Another :has() experiment:
Add a class such as featured to a heading: <h2 class="featured">Signal Station</h2>
Then try: .card:has(.featured) to give the entire card a different border or background.
This lets CSS respond to the contents of an element instead of requiring another class directly on the parent.
Why are these useful for components?
Container queries allow a reusable component to adapt to the space it receives. Modern selectors allow components to respond more intelligently to their own content.
Together, they can reduce the need for extra helper classes and page-specific layout rules.
Good modern CSS habit: Use new selectors when they make the stylesheet clearer. A newer feature is not automatically better if a simple class or selector already solves the problem cleanly.
👀 Show Solution
<style>
.card-container {
container-type: inline-size;
max-width: 600px;
margin: 30px auto;
padding: 15px;
border: 2px dashed #555;
}
.card {
background-color: #111;
border: 2px solid #8A8A8A;
border-radius: 10px;
padding: 20px;
}
.card :is(h2, h3) {
margin-top: 0;
}
.card :where(p, ul, ol) {
line-height: 1.6;
}
.card:has(button) {
border-style: solid;
}
@container (min-width: 450px) {
.card {
padding: 35px;
}
.card h2 {
font-size: 2rem;
}
}
</style>
<div class="card-container">
<article class="card">
<div class="card-content">
<h2>Signal Station</h2>
<p>
This card responds to the width
of its own container.
</p>
<button type="button">
Explore
</button>
</div>
</article>
</div>The .card-container provides the size being measured. When that container reaches 450 pixels wide, the @container rule changes the card inside it.
The :is() and :where() selectors group related selectors, while :has(button) lets the card react to the presence of a button inside it.
