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

Play in Editor

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.

Selectors Level 4 - Relational Pseudo-Class :has()