The CSS :has() Selector: The Parent Selector We Always Wanted
: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.