CSS pseudo-classPseudo-classes

:has()

Selects an element based on what it contains or what follows it. Lets you style a parent according to its children.

On this page

Example

#
CSS
.card {
  display: grid;
  gap: 10px;
}

.card:has(img) {
  grid-template-columns: 56px 1fr;
  align-items: center;
}
Preview

Definition and usage

#

The :has() pseudo-class checks whether something matching the given selector exists inside an element or next to it. .card:has(img) selects cards that contain an image, and form:has(:invalid) forms with at least one invalid field. For years, this kind of "parent selector" was considered impossible in CSS.

The selector inside the parentheses is relative to the element. With no combinator it means a descendant at any depth, with > only a direct child (ul:has(> li.active)), and with + or ~ a sibling further along: h2:has(+ p) is a heading followed by a paragraph. This way, :has() also gives you a previous-sibling selector.

The condition is live. label:has(input:checked) changes the label’s look the moment the field gets checked, and body:has(dialog[open]) can lock page scrolling while a dialog is open. Many tasks that used to require JavaScript are handled with a single rule.

Specificity is calculated the same way as in :is(): the weight of the strongest selector in the list. :has() can’t contain another :has() or pseudo-elements. Narrow it down to specific components, since the browser has to re-evaluate very broad rules often.

Syntax

#
Syntax
:has( <relative selector list> )

Example selectors

#

Example selectors in practice

#

.card:has(img)

Cards that contain an image. The text-only card stays gray.
.card:has(img) { background: #7c3aed; color: #fff; }

.card:has(input:checked)

Check the box in the third card: the whole card lights up, not just the field.
.card:has(input:checked) { background: #7c3aed; color: #fff; }

.card:not(:has(img))

The reverse condition: cards without an image.
.card:not(:has(img)) { background: #7c3aed; color: #fff; }

.demo:has(input:checked)

The container reacts to the state of a field lying inside it. Check the box and watch the border.
.demo:has(input:checked) {
  border-color: #a78bfa;
  background: rgb(124 58 237 / 0.15);
}

More examples

#
A form field with an error
CSS
.field:has(input:invalid) {
  border-color: #f43f5e;
}

.field:has(input:invalid) .error {
  display: block;
}
Preview

Type text without an @ sign and click outside the field. The border and message change at the level of the whole field, even though the error belongs to the input alone.

Browser support

#

Widely available · since 2023

Works in all modern browsers, phones included. You can use it without worry.

  • Chrome
  • Edge
  • Firefox
  • Safari

Good practices

#
  • Narrow :has() down to a component, e.g. .card:has(img). A bare :has(…) with nothing before the colon checks every element on the page.
  • Older browsers skip a rule with :has() entirely. Make sure the page still looks reasonable without it, and treat :has() as an enhancement, for example inside @supports selector(:has(a)).
  • Before adding a class to a parent from JavaScript, check whether :has() combined with a state pseudo-class, such as :checked, :focus-visible, or :invalid, is enough.

Related entries

#

Spotted a mistake or missing an example? Write to us.