👨👧 Parent Finally Knows About Child
CSS couldn’t select parent based on children. Until now. :has() changes everything — style parent when child exists.
❌ Before :has()
// JavaScript needed
if (card.querySelector('.badge')) {
card.classList.add('has-badge');
}
✅ With :has()
// Pure CSS!
.card:has(.badge) {
border-left: 4px solid gold;
}
🎯 Powerful Examples
// Style form that has an invalid input
form:has(input:invalid) {
border: 2px solid red;
background-color: #fff5f5;
}
// Highlight section containing an image
section:has(img) {
padding: 20px;
background: #f0f4f8;
}
// Remove margin from label that has input checkbox
label:has(input[type='checkbox']) {
margin-bottom: 0;
}
// Style card with featured badge
.card:has(.featured-badge) {
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.2);
transform: scale(1.02);
}
// Select parent that has specific text
article:has(p:contains('important')) {
border-left: 4px solid orange;
}
✅ Complex Selectors
// Parent containing an image AND a button
.gallery-item:has(img):has(button) {
position: relative;
}
// Parent with at least 3 children
.container:has(> :nth-child(3)) {
grid-template-columns: repeat(3, 1fr);
}
// Parent that contains element that contains text
.sidebar:has(:is(.warning, .error)) {
background-color: #fff3cd;
}
// Combining :not with :has
article:not(:has(img)) {
padding-left: 0; // No featured image
}
💡 Browser Support
- Chrome 105+ ✅
- Edge 105+ ✅
- Safari 15.4+ ✅
- Firefox 121+ ✅ (finally!)
- Use progressively — won’t break older browsers
“Wanted to highlight cards with discount badge. Before: JS loop. Now: one line of CSS. :has() is the most exciting CSS feature in years.”
