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.

Available for rescue and re-platforming work

I take over platforms that already exist and are in trouble. Node, TypeScript, React and Laravel, mostly in regulated or high-traffic environments. If something needs rescuing, re-platforming or finishing, my full history is on the CV.

Related Posts

Comments