The :has pseudo-class in CSS is a powerful selector introduced in CSS Selectors Level 4. This property allows developers to apply styles to an element based on the presence of its descendants or related elements, effectively enabling “parent” selectors, which was a long-requested feature in CSS.
What is the :has Property?
The :has property is a relational pseudo-class that matches elements containing elements that meet certain criteria. It’s particularly useful for styling an element if it contains specific child elements or if certain conditions within its subtree are met.
Syntax
The basic syntax of the :has pseudo-class is:
element:has(selector) {
/* styles */
}In this syntax, element represents the parent element, and selector represents the child or descendant elements being checked.
Practical Examples
Here are a few examples.
Example 1: Highlighting a Parent Element Based on a Child Element
Suppose we want to highlight a div element if it contains an img element. Here’s how we can do it:
<div>
<p>This div contains an image.</p>
<img src="https://via.placeholder.com/150" alt="Placeholder Image">
</div>
<div>
<p>This div does not contain an image.</p>
</div>div:has(img) {
border: 2px solid green;
padding: 10px;
}Example 2: Styling a List Item’s Parent Based on the Child Element
If we want to style a ul element differently when it contains a li element with a specific class, we can use the :has property:
<ul>
<li>Item 1</li>
<li class="highlight">Item 2</li>
<li>Item 3</li>
</ul>
<ul>
<li>Item A</li>
<li>Item B</li>
<li>Item C</li>
</ul>ul:has(li.highlight) {
background-color: lightyellow;
padding: 10px;
}Example 3: Form Validation Styles
We can also use the :has pseudo-class for form validation. For instance, to style an input element’s parent div if the input is invalid:
<form>
<div class="form-group">
<label for="email">Email:</label>
<input type="email" id="email" required>
</div>
<button type="submit">Submit</button>
</form>.form-group:has(input:invalid) {
border: 2px solid red;
padding: 10px;
}Comparison with JavaScript
Before :has, achieving the same effect required JavaScript – querying the DOM, checking for child elements, and applying styles programmatically. CSS handles this natively now, which means fewer event listeners and simpler code.
Here’s how you can achieve the same result of Example #1 with vanilla javascript:
document.addEventListener("DOMContentLoaded", function() {
const divs = document.querySelectorAll('div');
divs.forEach(div => {
if (div.querySelector('img')) {
div.style.border = '2px solid green';
div.style.padding = '10px';
}
});
});
The
:haspseudo-class is fast in modern browsers, but unconstrained selectors like:has(img)without a type prefix force the engine to check every element on the page. Prefer constrained selectors likediv:has(img)and use the direct child combinatordiv:has(> img)when you only care about immediate children.
Browser Support
The :has pseudo-class is supported by all major browsers since late 2023, covering roughly 93% of global users. Chrome 105+, Firefox 121+, Safari 15.4+, and Edge 105+ all handle it. You can use CSS @supports with @supports selector(:has(*)) for feature detection if you still need a fallback path.
FAQs
div:has(img) targets any div that contains an img.:has() handles parent-child styling natively in CSS without DOM queries or event listeners. JavaScript is still needed for logic beyond styling.:has(img) can slow rendering. Prefix with a type or class (div:has(img)) and use the direct child combinator (:has(> img)) to keep things fast.:has() composes with :not(), :is(), :where(), and state pseudo-classes like :checked or :invalid. For example, form:has(input:invalid) targets forms containing invalid inputs.Conclusion
The :has pseudo-class gives CSS something developers wanted for years – true parent selectors. I use it regularly, and it simplifies layouts that previously needed JavaScript for conditional styling. Browser support is solid across the board, so go ahead and start using it. If you’re exploring other modern CSS features, check out native CSS nesting as well.


