CSS `:has()` Isn’t New, But Most Developers Still Aren’t Using It Well
The first native AI agent for mobile is here.
Droidrun is the first native mobile AI agent that lets LLMs control iOS and Android devices. The framework is the most powerful option available for mobile agents. It is also fully open source.
The :has() selector is not a new CSS feature.
It has been supported long enough to be used in real projects.
Yet many developers still either avoid it or misunderstand what it is actually good for.
Let’s clear that up.
What :has() Really Does
:has() is a relational pseudo-class.
It allows CSS to select an element based on what it contains or what comes after it.
In simple terms, it lets CSS answer this question:
Style this element if that exists.
The Core Idea
.card:has(img) {
padding: 2rem;
}
Only cards that contain an image get extra spacing.
No utility classes
No JavaScript
No extra markup
Where :has() Actually Shines
1) Form State Styling
form:has(input:invalid) {
border: 2px solid red;
}
The form reacts to validation state automatically.
2) Checkbox Driven UI
label:has(input:checked) {
font-weight: bold;
}
The label updates when the checkbox is checked.
3) Content Aware Layouts
.article:has(h2) {
grid-template-columns: 2fr 1fr;
}
The layout adapts based on the content inside it.
4) Navigation State Styling
nav:has(a.active) {
background: black;
}
The navigation reacts to the active link without JavaScript.
Why This Matters
Before :has():
- Parents could not react to children
- JavaScript was required for simple UI state
- Markup had to carry extra classes
With :has():
- CSS reacts to DOM state
- HTML stays clean
- Less JavaScript is needed
Things to Be Careful With
- Avoid using
:has()on very broad selectors - Do not attach it to
*orbody - Use it for localized, intentional conditions
Used well, it is safe and predictable.
Final Takeaway
:has() does not replace JavaScript.
It replaces unnecessary JavaScript.
If you already support modern browsers, it deserves a place in your CSS toolkit.