Summary

  • In the W3C’s November 2018 Selectors Level 4 draft, :has() could describe an element by a relative match such as an image child or a following sibling, but the draft’s live CSS selector profile excluded it.
  • By May 2022 the draft had removed the live/snapshot profiles. It kept :has() as optional and at-risk; the November 2022 revision tightened its boundaries by disallowing nested :has() and ordinary pseudo-elements inside it.

Imagine a news site assembling article cards. Some cards carry a photograph. Some are all headline and deck. The outer box ought to give the photograph room when it exists, perhaps changing padding or background. A familiar CSS selector can reach down from the box to style the image. Reaching upward to choose the box itself has been the awkward part. The child arrived with a suitcase; the parent is still setting one place at the table.

The W3C’s 21 November 2018 Selectors Level 4 draft had an answer on paper: :has(). Its example a:has(> img) matches an anchor with an image as a direct child. The anchor is the element being selected. The image supplies the condition. The same draft gives dt:has(+ dt), matching a term followed immediately by another term. That second example is the clue that “parent selector,” however catchy, is too small a name. The test can look to a neighboring sibling as well as down into a family tree.

The 2018 draft also kept this power in a curious waiting room. It split selectors into a live profile, intended for browser CSS matching, and a snapshot profile, intended for less time-sensitive work on a static tree, with querySelector() as its example. :has() appeared in the snapshot profile and was excluded from live CSS. The text asked implementers to review that division. In the imaginary newsroom, an inspector could ask whether the card had an image, while the live stylesheet was told to leave the question alone. The feature had a name and examples, but no seat at the styling table under that draft’s rules.

The 7 May 2022 Working Draft changed that arrangement. Its change log says the selector profiles were removed; :has() was marked optional and at-risk instead. The categorical line between static queries and live CSS selection disappeared. Support remained a choice for conforming implementations. The rulebook had opened the door; each browser still had to decide whether to walk through it.

What would a stylesheet ask once the door opened? The 2022 draft keeps the useful small examples. a:has(> img) can select the link that directly contains an image. dt:has(+ dt) can select a definition term that has another term immediately after it. In both, the selector begins with the element whose style or matching result we care about; the argument supplies a relative test anchored to that element. The document tree stays as it is. The selected element can change its own outfit after checking who lives inside or next door.

The same draft offers a trap worthy of a copy editor: section:not(:has(h1, h2, h3, h4, h5, h6)) describes a section with no heading descendants. Reorder the parentheses to section:has(:not(h1, h2, h3, h4, h5, h6)) and the question changes. Now the section can match because it contains some descendant that is not a heading. One pair of brackets moves the negation from “there is no heading” to “there is something other than a heading.” A section with a fine headline and an ordinary paragraph can satisfy the second test. One misplaced bracket can turn a useful rule into a small editorial emergency.

The 11 November 2022 revision drew firmer edges around the idea. It forbids nesting :has() inside :has() and generally bars pseudo-elements as arguments. The document explains the concern with pseudo-elements: some exist conditionally because of ancestor styling, so asking an ancestor to depend on those generated pieces could create a cycle. The feature became more expressive by stating where its curiosity had to stop. The family meeting does not need a family meeting inside every answer.

The W3C publication history places these drafts in 2018 and 2022. Across them, the interesting shift is from a relationship the selector language could describe but live CSS was barred from using, to a relationship the draft could offer to stylesheets under an optional support rule. The browser evaluates the relationship. The author chooses which relationship matters: an image inside a card, a sibling beside a term, or a missing heading inside a section. That choice can remove the little bits of extra markup and script sites once used just to tell an outer box about its contents.

The parent selector nickname survives because everyone recognizes the family argument. The fuller story is better: CSS learned to ask a question about the neighborhood before dressing the house. Sometimes the children write home. Sometimes the next-door neighbor does. Either way, the outer card can finally stop pretending it lives alone.