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.
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.