:has()

The Parent Selector, Finally

:has()

`:has()` matches an element based on what's INSIDE it. A card with an image, a form with an error, a paragraph with a link.

3 min read Level 2/5 #css#has#pseudo-class
What you'll learn
  • Use `:has()` to style based on children
  • Combine with `:not()` for absence
  • Recognize browser support

:has() is the long-awaited parent selector. Match an element if it contains something.

Examples

/* Cards that have an image get extra top padding */
.card:has(img) { padding-top: 0; }

/* Forms with errors get a red border */
form:has(.error) { border: 2px solid red; }

/* List items with a checked checkbox */
li:has(input:checked) { text-decoration: line-through; }

/* Paragraphs containing a link */
p:has(a[href]) { /* ... */ }

Without — Negation

.card:not(:has(img)) {
  padding-top: 1rem;
}

article:has(:not(h2)) {   /* anything other than h2 inside */
  /* ... */
}

Relational Selector

:has() accepts complex selectors. The selector is RELATIVE to the element on the left:

section:has(> h2 + p) { ... }     /* h2 directly followed by p */
.row:has(.col.featured) { ... }

A Powerful Real Example

A nav whose hamburger only shows when a sibling input is checked:

header:has(input.menu-toggle:checked) .nav {
  display: block;
}

Pure-CSS menu toggle — no JS.

Browser Support

Excellent in all modern browsers as of 2024. Safe to use today.

Performance

The browser can optimize :has(), but very broad selectors (*:has(...)) can be slow on huge pages. Aim selectors at specific elements.

Up Next

:is() and :where() — selector grouping and specificity control.

:is() and :where() →