CSS :has() Is Finally Here

· 1 min read
css selectors

The Problem

Styling a parent based on its children required JavaScript:

// Add class to card if it contains an image
document.querySelectorAll('.card').forEach(card => {
  if (card.querySelector('img')) {
    card.classList.add('has-image');
  }
});

Then style .card.has-image. Clunky.

The Solution

.card:has(img) {
  aspect-ratio: 16/9;
}

.card:has(.badge) {
  padding-top: 2rem;
}

.form:has(:invalid) {
  border-color: red;
}

Why It Works

:has() is a relational pseudo-class. It selects elements that contain descendants matching the selector argument.

The web platform lacked this for over two decades because it was considered too expensive to implement. Browsers finally figured it out.

Common patterns:

/* Parent of focused input */
.input-group:has(:focus) { ... }

/* Previous sibling (yes, really) */
.item:has(+ .item:hover) { ... }

/* Conditional layout */
.grid:has(> :nth-child(4)) { ... }

Browser support is now universal in evergreen browsers. Finally.


I’ve wanted this since I started writing CSS. Some waits are worth it.

Related Posts

Comments