The Relational Pseudo-Class Arrives

For more than a decade, developers have asked for a CSS parent selector. Browser vendors have long cited performance concerns as the main obstacle. Those concerns have faded as rendering engines have become more sophisticated at determining exactly what needs to be painted or updated. Brian Kardell of Igalia recently announced early prototyping of the :has selector, which could finally address this long-standing request.

Although the developer community calls :has a "parent selector," the specification defines it more accurately as a relational pseudo-class. Its range goes beyond simply styling a parent element based on its children. It also works to style preceding siblings based on what follows them, and to react to deeply nested content states.

Where a Relational Selector Fits

Content-Based Component Variations

UI components often have multiple variations determined by their content: whether they include an image, a caption, a description, or something else. Today that means manually maintaining a cluster of CSS modifier classes on the parent, or using JavaScript to add them at runtime. Naming methodologies like BEM also require careful documentation, often via Storybook or similar tooling, so that every variation is applied correctly.

The relational selector would let you encode those checks directly in CSS. Styles would apply automatically based on what's inside the element, reducing the number of variation classes and the change of human error. The selectors themselves become self-documented condition checks.

Validation and Error State Styling

Native form validation relies on pseudo-classes such as :valid and :invalid, which work with HTML attributes like pattern and required. The limitation is that those pseudo-classes target only the input itself or its adjacent element. In many designs, the wrapping container or a preceding label also needs state-specific styling, which current CSS cannot reach.

The same applies when an API returns error messages appended inside the input container. With :has, you could write a check for a non-empty message child and style the container accordingly, eliminating the need to toggle a container class from JavaScript.

Reacting to Child State

Beyond validation, parent or preceding element styles often depend on states such as :checked, :disabled, :hover, or :visited. The relational selector generalizes these patterns, allowing CSS to respond to the availability of an attribute, class, element type, or any targetable state, regardless of where it sits in relation to the element you want to style.

Previous Sibling Selection

CSS selection famously moves only one direction: down to descendants or forward to following siblings. A relational selector reverses this behavior. It can anchor on a target element's condition and send styles backwards to a preceding sibling, acting as a true previous-sibling selector.

Extending :empty for Dynamic Content

Skeleton loaders and dynamically populated content typically require a loading class toggled on the parent via JavaScript once data arrives. With a relational selector, you could write a condition that simply checks for the presence of the required child elements and their content. The styles would update automatically as the DOM populates, covering even deeply nested response structures.

Syntax and Specification Status

As of writing, :has is not supported in any browser. The early definition lives in the selectors level 4 specification. The syntax resembles other pseudo-classes:

The :has() function takes a regular CSS selector as its argument. Styles apply to the target element only when that argument selector matches something scoped to it. In practice, this means the condition is relative to the element you wish to target:

  • <target_element>
    The element that will be styled if the condition passes. The argument selector is scoped to this element.
  • <selector>
    The condition that must be met for the target to get its styles.

As with other pseudo-classes, this can combine with others like :not() for more expressive checks. In short, the relational selector anchors CSS selection to the element carrying the :has pseudo-class and prevents selection from jumping to the elements passed as its argument.

Workarounds in Use Today

Variation Classes for Static Layouts

For static content that doesn't change after initial render, developers manually apply modifier CSS classes to the parent element. Card components with and without images or captions are a typical example. This approach works but requires documentation and can lead to creative HTML workarounds just to keep classes organized when an element carries a large number of modifiers.

JavaScript for Dynamic Updates

For content whose state will change, JavaScript remains the fallback. Developers write custom solutions on a case-by-case basis to synchronize parent element classes with child state.

The jQuery library has actually implemented a relational :has selector since 2007, following the newer CSS specification. It still suffers from the unavoidable weaknesses of the JavaScript approach: the selector is invoked from JS event listeners, relies on external dependencies and parsing time, and leaves users with JavaScript disabled exposed to visual errors unless you also maintain fallback styles.

Why :has Changes the CSS Game

The arrival of the :has() relational pseudo-class is a significant milestone, comparable in impact to container queries. It enables selectors that were previously impossible in pure CSS, giving developers the ability to style an element based on its children, descendants, or even its following siblings.

For years, the absence of a parent selector forced engineers into a familiar workaround: sprinkle modifier classes across the markup, often toggled by JavaScript when a child's state changed. :has() largely removes that burden. Instead of manually appending a class like .card--selected when a checkbox inside the card is checked, you can write a rule that targets the card directly. The style logic stays in CSS, reducing the need for scripting and the chance of mismatched class strings.

The relational selector also makes stylesheets more self-documenting. A rule such as .form-row:has(input:focus) clearly states a visual emphasis is tied to the focus state of an input within that row, rather than an arbitrarily named class.

The broader implications go beyond just a parent selector. The spec also supports previous sibling selection via combinations like .x:has(~ .y), opening up structural styling patterns that required complex JavaScript or full re-renders in the past. It is a more robust, semantic foundation for dealing with component state.

As the "Can I :has" reference explains, this isn't just about convenience. It normalizes an entire category of UI logic in CSS, pushing web development toward less markup choreography and more explainable style rules.

Open Questions & Resources

With the selector now arriving in browsers, the main remaining question for the community: what are the creative and practical edge cases? Beyond the obvious checkbox-to-label patterns, plenty of layouts governed by content presence or validation state could benefit. What else does this unlock for your own components—and what workarounds have you been maintaining that you'll now delete?

Those interested in the spec's internals and evolution should check out the official documentation and historical discussions:

Smashing Editorial