CSS :has() Pseudo-Class
The CSS :has() pseudo-class selects an element based on whether it contains or is related to elements matching a specified selector. It allows CSS to style parent elements, containers, and other elements according to their contents or relationships without requiring JavaScript.
:has() Syntax
selector:has(relative-selector) {
property: value;
}
The :has() pseudo-class accepts a relative selector list. It matches an element when at least one selector in that list finds a matching element in the required relationship to it.
Example Syntax
.card:has(img) {
border: 2px solid #0088cc;
}
This rule selects any element with the .card class that contains an <img> descendant.
Selecting Direct Children
.card:has(> img) {
padding: 15px;
}
The child combinator (>) limits matching to cards that have an image as a direct child.
Selecting Related Siblings
h2:has(+ p) {
margin-bottom: 5px;
}
This rule selects an <h2> element when its immediately following sibling is a paragraph.
How :has() Works
The :has() pseudo-class evaluates relationships between an element and other elements in the document.
The element before :has() is the subject being selected. The selector inside the parentheses determines which related elements must exist for that subject to match.
Matching Descendants
<div class="card">
<h3>CSS Selectors</h3>
<p>Learn how selectors work.</p>
</div>
.card:has(p) {
background-color: #eaf6fc;
}
The card matches because it contains a paragraph.
Dynamic Matching
Matching updates as the document changes. For example, if a matching element is added or removed, the styles applied through :has() can update automatically.
Matching Interactive States
.form-group:has(input:focus) {
border-color: #0088cc;
}
This rule selects a form group while it contains a focused input.
Unlike a simple descendant selector, :has() allows the containing element to receive the style.
Selecting Parent Elements
One of the most useful features of :has() is its ability to select a parent element according to the elements it contains.
Cards Containing Images
.card:has(img) {
border-color: #0088cc;
}
Only cards containing an image receive the border color.
Articles Containing Headings
article:has(h2) {
padding-top: 20px;
}
Articles containing an <h2> descendant receive additional top padding.
Lists Containing Active Items
ul:has(.active) {
border-left: 3px solid #0088cc;
}
A list receives a left border when it contains an element with the .active class.
Not Limited to Parents
Although :has() is often called a parent selector, it can also match elements based on following siblings and other relative relationships.
Relative Selectors
The selector inside :has() can use combinators to describe different relationships.
Common Relative Selector Patterns
| Selector | Matches When |
|---|---|
div:has(p) |
The div contains a paragraph descendant. |
div:has(> p) |
The div has a paragraph as a direct child. |
h2:has(+ p) |
The h2 is immediately followed by a paragraph. |
h2:has(~ p) |
The h2 has a following paragraph sibling. |
.card:has(img:hover) |
The card contains a hovered image. |
form:has(input:invalid) |
The form contains an invalid input. |
Adjacent Sibling Example
<h2>CSS Tutorial</h2>
<p>Learn the basics of CSS.</p>
h2:has(+ p) {
margin-bottom: 8px;
}
The heading matches because a paragraph immediately follows it.
General Sibling Example
h2:has(~ .note) {
color: #006699;
}
This rule selects a heading when a later sibling has the .note class.
Multiple Selectors
The :has() pseudo-class accepts multiple relative selectors separated by commas. An element matches if any one of the selectors matches.
Matching Images or Videos
.media-card:has(img, video) {
padding: 20px;
}
The card matches if it contains an image, a video, or both.
Requiring Multiple Conditions
.card:has(img):has(.caption) {
border: 2px solid #0088cc;
}
Chaining two :has() pseudo-classes requires both conditions to be satisfied.
Combining Different Relationships
.card:has(> img, > video) {
background-color: #f2f8fc;
}
The card matches when an image or video is a direct child.
Important Selector List Behavior
The selector list inside :has() is not forgiving. If one selector in the list is invalid, the entire :has() expression becomes invalid.
Use :is() or :where() inside :has() when a forgiving selector list is needed.
Form States
The :has() pseudo-class can style forms and containers according to the state of their controls.
Highlighting Invalid Fields
.form-group:has(input:invalid) {
border-color: #cc3333;
}
The form group receives a red border when its input is invalid.
Highlighting Checked Options
.option:has(input:checked) {
background-color: #eaf6fc;
border-color: #0088cc;
}
The option container changes appearance when its checkbox or radio button is checked.
Highlighting Focused Controls
.form-group:has(input:focus-visible) {
background-color: #f0f8ff;
}
The form group changes background when an input inside it matches :focus-visible.
Showing a Form Status
form:has(input:invalid) .form-status {
display: block;
}
This rule displays an existing status element when the form contains an invalid input. The status message itself must be included in the HTML.
:has() vs. :is() and :where()
These functional pseudo-classes all accept selectors, but they serve different purposes.
Comparison
| Pseudo-Class | Purpose |
|---|---|
:has() |
Matches an element based on related elements. |
:is() |
Matches an element against a list of selectors. |
:where() |
Matches an element against a list of selectors with zero specificity contribution. |
Example
.card:has(img) {
border: 2px solid #0088cc;
}
:is(h2, h3) {
color: #003366;
}
:where(article, section) {
line-height: 1.6;
}
The first rule selects cards containing images. The second selects headings matching either selector. The third selects articles and sections without adding specificity from :where().
Combining :has() with :is()
.card:has(:is(img, video)) {
padding: 15px;
}
This rule matches cards containing either an image or a video.
:has() Specificity
The specificity of :has() is determined by the most specific selector in its argument list. The pseudo-class itself does not add an additional specificity value.
Specificity Examples
| Selector | Specificity |
|---|---|
:has(img) |
0-0-1 |
.card:has(img) |
0-1-1 |
.card:has(.featured) |
0-2-0 |
.card:has(#main-image) |
1-1-0 |
.card:has(img, #main-image) |
1-1-0 |
Highest Specificity Wins
.card:has(img, #featured-image) {
border-color: #0088cc;
}
The argument list contains an element selector and an ID selector. The ID selector determines the specificity contribution even when the image selector is the one that matches.
Reducing Specificity
:where(.card):has(img) {
border-color: #0088cc;
}
Wrapping the class selector in :where() removes its specificity contribution, leaving the specificity contributed by the img selector.
Limitations
The :has() pseudo-class is powerful, but its arguments have specific restrictions.
No Nested :has()
/* Invalid */
.card:has(.content:has(img)) {
border-color: #0088cc;
}
A :has() pseudo-class cannot be nested inside another :has().
Pseudo-Elements in :has()
Pseudo-elements generally cannot be used inside :has() unless a specification explicitly permits them.
/* Invalid */
.card:has(::before) {
color: #0088cc;
}
Performance Considerations
Complex selectors involving large descendant trees or frequently changing states can require additional browser matching work.
Prefer targeted selectors such as .card:has(> img) when they express the intended relationship clearly.
Not a JavaScript Replacement
The :has() pseudo-class controls selector matching and styling. It does not create elements, change application data, or perform event-driven actions.
Accessibility
The :has() pseudo-class can improve visual feedback for forms and interactive components without adding JavaScript.
Highlighting Selected Options
.option:has(input:checked) {
border: 2px solid #0088cc;
background-color: #eaf6fc;
}
This makes a selected option easier to identify.
Preserving Focus Indicators
.form-group:has(input:focus-visible) {
background-color: #eaf6fc;
}
input:focus-visible {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
The containing form group is highlighted while the focused input retains a visible focus indicator.
Semantic HTML
Use appropriate form controls, labels, and semantic elements. CSS selection does not replace accessible HTML structure.
Best Practices
- Use
:has()when an element's styling depends on related elements or their states. - Use child and sibling combinators to describe relationships precisely.
- Keep selector arguments readable and avoid unnecessary complexity.
- Remember that comma-separated arguments match when any one condition is satisfied.
- Use chained
:has()expressions when multiple conditions must be true. - Consider specificity when combining
:has()with classes and IDs. - Do not nest
:has()inside another:has(). - Preserve accessible focus indicators and semantic HTML.
:has() Example
This example uses :has() to highlight selected options. Select a checkbox to change the border and background of its containing option.
HTML
<div class="has-demo">
<h3>Choose Your Topics</h3>
<label class="option">
<input type="checkbox" name="topic" value="html">
HTML
</label>
<label class="option">
<input type="checkbox" name="topic" value="css">
CSS
</label>
<label class="option">
<input type="checkbox" name="topic" value="javascript">
JavaScript
</label>
</div>
CSS
.has-demo .option {
display: block;
padding: 12px;
margin-bottom: 8px;
border: 2px solid #ccc;
background-color: #fff;
cursor: pointer;
}
.has-demo .option:has(input:checked) {
border-color: #0088cc;
background-color: #eaf6fc;
}
.has-demo .option:has(input:focus-visible) {
outline: 2px solid #005fcc;
outline-offset: 2px;
}
.has-demo input {
margin-right: 8px;
}
Browser Result
Note: The example uses CSS only. No JavaScript is required to update the selected option styles.
Browser Support
Baseline: Newly available. The :has() pseudo-class is supported in current versions of major browsers, including Chrome, Edge, Firefox, and Safari. Support became broadly interoperable in December 2023.
For current compatibility information, visit Can I Use? .
Specifications
The :has() pseudo-class is defined in CSS Selectors Level 4 as the relational pseudo-class. The specification defines its relative selector arguments, matching behavior, specificity, and restrictions.
