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.