What :has()
changed in CSS?
The long-awaited :has()
pseudo-class has finally landed in Firefox, meaning it’s now available in all the major browsers ready to be used in production. :has()
is part of the CSS Selectors Level 4 specification, which represents the latest addition to the already big set of available CSS selectors. CSS Selectors Level 4 is a set of powerful new selectors and features to the CSS language, including :matches()
, :not()
and :is()
.
So, what’s so special about :has()
? In simple terms, it lets you style elements in a new way. Usually, in CSS, you style an element based on what it is or where it is. With :has()
, you can now style an element based on what’s inside of it. Meaning if an element contains a certain thing, like an image or a link, you can change the style of the outer element because of that.
The syntax
:has() can take a list of CSS selectors as its parameters
:has(<CSS selectors>) {
/* Add some fancy CSS */
}
To bring this to life, let’s dive into some practical examples that illustrate how :has()
can be applied in various scenarios.
Parent has a specific element
Style a list item differently if it contains a link.
li:has(a) {
background-color: tomato;
padding: 0.5rem;
color: white;
}
This targets li
elements that contain at least one element, applying a specific background color, padding and color, thereby visually differentiating list items that are links.
Let’s see it in action
Has either or both of the elements
Style a .card
element if it contains either a video
or a picture
element or both.
.card:has(video, picture) {
border: 3px solid tomato;
padding: 0.5rem;
}
This CSS rule targets .card
elements that contain at least one video
or picture
element. The presence of either or both of these elements triggers the styling.
Let’s see it in action
Parent has both elements
Style a card only if it contains both an image and a label.
.card:has(img):has(.label) {
border: 3px solid tomato;
}
The .card
element will be styled with a border only if it contains both an img
element and an element with the class .label
Let’s see it in action
Does Not Have the Element
Style a menu item if it does not have a submenu (indicated by the absence of a ul
element).
nav li:not(:has(ul)) {
background-color: tomato;
padding: 0.5rem;
}
This rule targets li
elements within a nav
that do not contain a ul
, applying the styling. It’s useful for differentiating between elements.
Let’s see it in action
Select previous element with adjacent sibling combinator
Typically, the adjacent sibling combinator is used to style an element that directly follows another. However, with a creative twist, we can actually use it to style a preceding list item.
li:has(+ .label) {
background-color: tomato;
}
In this example, an <li>
element is styled if it is immediately followed by an element with the class .label
.
Let’s see it in action
In summary, the new :has()
feature in CSS is a game-changer. It let us style elements based on what's inside them. And the best part? You can mix :has()
with other CSS, like :not()
, :empty
, :hover
, :valid
or :invalid
, :checked
and many more to make CSS even more powerful.