The :has() CSS pseudo-class selects elements that contain other elements that match the selector passed in its arguments. It is often referred to as the «parent selector» because of its ability to select a parent element based on the child elements it contains and apply styles to the parent.
/* Select the .card element when it
contains a
followed by a paragraph. */
.card:has(figure + p) {
flex direction: row;
}
This example selects an element with class .card if it contains a
element:
This is incredibly useful when styling components that may or may not contain certain elements inside, such as a grid of cards where a card element always has a paragraph but may not have an accompanying image.
Image or no image? That is the question.
Thus, in situations where you don’t always know what the markup includes, you can still write styles that meet those conditions.
:has() is defined in the Selectors Level 4 specification, where it is described as «the relational pseudo-class» because of its ability to match selectors based on an element’s relation to other elements.
Basic usage
The following HTML contains two
Let’s say you only want to style the
:has() is perfect for the job:
button:has(svg) {
/* Styles */
}
The :has() selector gives us the ability to distinguish between a button that has a
The unforgiving selector list is a comma-separated list of elements that are evaluated together based on their relationship to the parent element.
article:has(ol, ul) {
/* Corresponds to an
ordered or unordered list. */
}
A bit later, we’ll take a closer look at the «unforgiving» nature of the argument list.
Specificity
One of the most interesting aspects of :has() is that its specificity is determined by the most «specific» element in the argument list. Let’s say we have the following style rules:
article:has(.some-class, #id, img) {
background: #000;
}
article .some-class {
background: #fff;
}
We have two rules, each of which selects the
to change its background. What is the background of this HTML?
You might think that there will be a white background (#fff) because it appears later in the cascade. But since the argument list for :has() includes other selectors, we must look at the most specific of them to determine the real specifics of this first rule. In this case it will be #id.
Let’s compare them:
article:has(.some-class, #id, img) generates score (1,0,1)
article.some-class generates score (0,1,1)
The first rule wins! The element gets a black (#000) background.