The CSS :has() selector lets you style an element when it contains, follows, or relates to another matching element. It is often called a relational selector because it can select a parent based on what is inside it.
Before :has(), many parent-state styling problems needed JavaScript. Now you can solve several of them directly in CSS, such as highlighting a card that contains a checked checkbox or styling a heading when a paragraph follows it.
CSS :has() Syntax
Write :has() after the element you want to style. Inside the parentheses, add the selector that must match.
Syntax:
selector:has(condition) {
property: value;
}
| Selector | Meaning |
|---|---|
| article:has(img) | Selects an article that contains an image |
| label:has(input:checked) | Selects a label with a checked input inside |
| h2:has(+ p) | Selects an h2 immediately followed by a paragraph |
| form:has(:invalid) | Selects a form that contains invalid fields |
Style a Parent Element
The most common use of :has() is styling a parent when it contains a specific child element.
Example:
/* Style a card only when it contains an image. */
.card:has(img) {
border-color: royalblue;
background-color: #f2f6ff;
}
Style a Selected Item
You can combine :has() with form states such as :checked. This lets you style the whole row when a checkbox or radio button changes.
Example:
/* Highlight the label that contains a checked input. */
.option:has(input:checked) {
color: seagreen;
font-weight: bold;
}
Validate Forms with CSS
:has() can detect invalid fields inside a form or section. This helps you show a visual warning without writing a custom validation script.
Example:
/* Add a warning border when any field is invalid. */
.signup-form:has(:invalid) {
border: 2px solid crimson;
}
Use Relative Selectors
The selector inside :has() can describe a relationship. For example, + p means an adjacent paragraph that comes immediately after the selected element.
Example:
/* Reduce spacing when a paragraph directly follows the heading. */
h2:has(+ p) {
margin-bottom: 6px;
}
Runnable Browser Example
This example styles each list item when its checkbox is selected. The parent list item changes color without JavaScript.
Example:
<ul class="task-list">
<li>
<label>
<input type="checkbox">
<span>Review landing page copy</span>
</label>
</li>
</ul>
<style>
/* Style the parent li when its checkbox is checked. */
.task-list li:has(input:checked) {
background: #ecfff2;
border-color: seagreen;
}
</style>
Specificity of :has()
The :has() selector takes the specificity of the most specific selector inside it. This behavior is similar to :is() and :not().
Example:
/* The #featured selector increases specificity. */
.card:has(#featured) {
border-width: 3px;
}
Practical Guidelines
- Use
:has()when CSS can express the relationship clearly. - Keep selectors short because complex relational selectors can be harder to maintain.
- Use it for parent styling, form state, and layout adjustments.
- Check browser support if your audience includes older browsers.
- Avoid using
:has()as a replacement for business logic that belongs in JavaScript.
Tip: Start with simple parent-state selectors. They are easier to read and usually give the biggest improvement over JavaScript-only styling.
Conclusion
The CSS :has() selector makes parent-aware styling possible in CSS. Use it to highlight elements based on children, form states, and nearby elements while keeping your styles clear and maintainable.