The CSS :has() Selector: The Parent Selector We Always Wanted

A
Author
30 Aug, 2026 1 min read 765 views

:has() matches an element if it contains something that matches the selector inside the brackets. For the first time, CSS can style a parent based on its children.

Cards with or without an image

.card:has(img) {
  grid-template-rows: 200px auto;
}

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

Form groups with errors

.field:has(input:user-invalid) label {
  color: var(--danger);
}

.field:has(input:focus-visible) {
  outline: 2px solid var(--brand);
}

:user-invalid only matches after the user has interacted with the field, so the form doesn't start out covered in red.

Style the page from a checkbox

/* Turn off scrolling while a dialog is open */
body:has(dialog[open]) {
  overflow: hidden;
}

/* A theme switch without JavaScript */
html:has(#dark-toggle:checked) {
  color-scheme: dark;
}

Previous-sibling selection

/* Make the item before a hovered item slightly bigger */
li:has(+ li:hover) {
  transform: scale(1.05);
}

Quantity queries

/* Switch to a 3-column grid when there are 6 or more items */
.gallery:has(> :nth-child(6)) {
  grid-template-columns: repeat(3, 1fr);
}

Performance note

Browsers optimise :has() well, but very broad selectors such as *:has(*) on large pages can be expensive. Anchor it to specific elements or classes.

Was this article helpful?

Comments

No comments yet. Have a question or a tip? Be the first to share it.

Leave a comment

Comments are reviewed before they appear.