The CSS :has() pseudo-class is one of the most powerful modern additions to CSS. It allows you to style an element based on its children, descendants, or even its siblings. This means CSS finally behaves like a **parent selector**, enabling patterns that were previously possible only with JavaScript.
Note
β Selects an element that contains another selector
β Enables state-based UI patterns
β Part of CSS Selectors Level 4
π¦ Syntax
:has() syntax
selector:has(selector) {
/* styles */
}The element before :has() is the one being styled; the selector inside :has() is the condition.
π¨ 1. Style a Parent if It Contains a Specific Child
Highlight card if it has an image
.card:has(img) {
border-color: #3498db;
background: #eef7ff;
}The .card gets styled only if it contains an <img>.
π¨ 2. Style a Form Group If an Input Is Invalid
Invalid form group
.form-group:has(input:invalid) {
border-left: 4px solid red;
}No JS needed for highlighting error fields.
π¨ 3. Style a Section Based on Hover of a Child
Parent reacts to child hover
.menu:has(.menu-item:hover) {
background: #f0f0f0;
}π¨ 4. Select Siblings Using :has()
Style label when input is checked
label:has(+ input:checked) {
font-weight: bold;
}The label changes when its sibling input is checked.
π¨ 5. Accordion / Toggle Behavior Without JavaScript
CSS-only accordion
.accordion:has(input:checked) .content {
max-height: 300px;
opacity: 1;
}A pure CSS accordion powered by :has().
π¨ 6. Change Layout If a Section Contains Many Items
Dynamic layout
.grid:has(.item:nth-child(5)) {
grid-template-columns: repeat(3, 1fr);
}If 5 or more items exist, switch the layout.
π¨ 7. Add Styles When Element Has No Children (inverse)
Empty state handling
.todo-list:not(:has(li)) {
background: #f9f9f9;
color: #aaa;
text-align: center;
}π§ͺ Real-World Use Cases
1οΈβ£ Valid/Invalid Form with No JS
Valid state
.input-group:has(input:valid) {
border-color: green;
}2οΈβ£ Navbar with Active Link
Active menu highlight
nav:has(a.active) {
border-bottom: 3px solid #3498db;
}3οΈβ£ Card with CTA Button
Card highlight
.card:has(.primary-btn) {
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}β οΈ Performance & Best Practices
- β Avoid using :has() in global selectors like *:has()
- β Donβt over-nest selectors inside :has()
- β Great for UI components, not general resets
- β Use it for specific conditions, not broad patterns
Note
π₯ Summary
The :has() selector brings parent-based styling to CSS, unlocking powerful UI patterns without JavaScript. It enables dynamic, responsive, and reactive styling based purely on HTML structure.