I want to style a card differently when it contains an image, or highlight a form row when its checkbox is checked, without JavaScript. I read that :has() is the parent selector. How does it work and is it safe to use?
DEV3 Developer Forum
How do I use the CSS :has() selector and native nesting? Can CSS select a parent now?
Ask a question
Every post is reviewed first. Usually reviewed within 24 hours.
dev3 ·
0
1 reply
dev3 · Accepted answer
Yes, :has() is the relational (parent) selector
It is supported in all current evergreen browsers (Chrome 105+, Safari 15.4+, Firefox 121+). It selects an element based on its descendants or following siblings.
/* card that contains an image gets a different layout */
.card:has(img) { display: grid; grid-template-columns: 120px 1fr; }
/* highlight the row whose checkbox is checked */
.row:has(input[type="checkbox"]:checked) { background: #eff6ff; }
/* field with an invalid input */
.field:has(input:user-invalid) label { color: #b91c1c; }
/* previous sibling trick: label before a focused input */
label:has(+ input:focus) { color: #2563eb; }
/* style the whole page when a modal is open */
body:has(dialog[open]) { overflow: hidden; }
Quantity queries
.list:has(> li:nth-child(4)) { columns: 2; } /* at least 4 items */
Native nesting
CSS nesting (no Sass needed) works in all current browsers:
.card {
padding: 1rem;
border-radius: 8px;
&:hover { box-shadow: 0 4px 16px rgb(0 0 0 / .12); }
& h2 { font-size: 1.25rem; }
.compact & { padding: .5rem; }
@media (min-width: 768px) { padding: 1.5rem; }
}
Relaxed nesting means you can nest type selectors without & in current versions, but keeping & is clearer and compatible with older engines.
Tailwind
has-[img]:grid, has-[:checked]:bg-blue-50, and for state of a sibling use peer/group variants. Tailwind 4 also provides in-* and not-* variants.
Pitfalls
:has()cannot be nested inside another:has().- Heavy selectors like
html:has(...)with expensive descendants can cost on huge DOMs; scope to a component. - Provide a sensible default when unsupported: the base style should work without
:has, and the rule enhances it.
0