Ronalds Vilciņš

CSS :has() Can Select a Parent

CSS :has() lets a selector match an element based on what is inside or next to it. It is often described as a parent selector, although relative selectors make it useful for more than parents.

This selector matches an <a> only when it has a direct <img> child:

a:has(> img)

The > matters. Without it, an image anywhere inside the link would match.

Looking at the next sibling

:has() can also look forward in the document. This matches a <dt> immediately followed by another <dt>:

dt:has(+ dt)

That makes it possible to style the first element based on a sibling that comes after it—something ordinary CSS selectors could not previously do.

Finding sections without a heading

Combine :has() with :not() to find sections missing the expected heading elements:

section:not(:has(heading-display, heading-section, heading-card, heading-small, h5, h6))

The placement of :not() changes the meaning. This version is not equivalent:

section:has(:not(heading-display, heading-section, heading-card, heading-small, h5, h6))

It matches a section containing any descendant that is not one of those headings, which will be most non-empty sections. Parentheses are doing quite a lot of work here.

Browser support changed after publication

When this article was published in December 2021, Safari Technology Preview 137 had enabled :has() by default, Chromium was still implementing it, and Firefox did not support it. Browser support later improved, so check current compatibility before deciding whether a fallback is needed.

:has() removes JavaScript from several small conditional styling jobs. I would keep the selector focused, though; a readable parent rule is useful, while a long chain of nested conditions becomes difficult to understand surprisingly quickly.

Enjoyed reading this?

If you found this article helpful, consider supporting my writing. Every little bit helps keep this blog going.

← Back to all articles