Container Queries & :has()
Two of the most impactful recent CSS additions solve problems that previously required JavaScript workarounds entirely.
Container queries style an element based on the size of its containing element, rather than the overall browser viewport — solving a long-standing gap in component-based design.
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
@container card (max-width: 399px) {
.card {
display: block;
}
}
A regular @media query only knows the browser window's size — it can't tell whether a .card component sits in a wide main column or a narrow sidebar. @container queries fix this: the same .card component can respond to its own available space, laying out horizontally in a wide container and stacking vertically in a narrow one, regardless of overall page width.
:has() is a "parent selector" — something CSS lacked for its entire history until recently, letting a selector match based on what's inside it.
/* Style a form group only if it contains an invalid input */
.form-group:has(input:invalid) {
border-left: 3px solid red;
}
/* Style a card differently if it contains an image */
.card:has(img) {
padding-top: 0;
}
/* Dim all cards except the one being hovered */
.gallery:has(.card:hover) .card:not(:hover) {
opacity: 0.5;
}
Before :has(), styling a parent based on its children's state required JavaScript to add/remove a class manually. :has() moves that logic entirely into CSS — a genuinely new capability, not just a shorter way to write something already possible.