css :has() selector - the parent selector we always wanted - By Sourav Mishra (@souravvmishra)
okeyy, so let's talk about the css :has() selector. it's the parent selector we've been begging for. here's how to use it with some real-world examples.
for years, we css devs asked for one thing: a parent selector. we wanted to style a parent based on its kids. and for years, they said "use javascript."
then :has() arrived, and it changed a hell lot of things.
what exactly is :has()?
so the :has() selector lets you pick an element based on its children or siblings. it's basically asking: "does this element have a child that matches this thing?"
/* select any card that contains an image */
.card:has(img) {
padding: 0;
}
/* select a label when its input is focused */
label:has(+ input:focus) {
color: blue;
}
this was literally impossible before without js.
how i use it in real life
1. form field styling
styling a form group based on input state is super easy now.
/* highlight the entire field group when input is focused */
.form-group:has(input:focus) {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}
/* show error styling on the group when input is invalid */
.form-group:has(input:invalid) {
border-color: #ef4444;
}
2. card layouts
we can tweak card styles based on what's inside them.
/* cards with images get no top padding */
.card:has(> img:first-child) {
padding-top: 0;
}
/* feature cards get highlighted */
.card:has(.featured) {
border: 2px solid gold;
}
3. empty states
showing placeholders when content is empty is a breeze.
/* show empty state when list has no items */
.todo-list:not(:has(li))::after {
content: "no tasks yet!";
color: #666;
}
combining :has() with :not()
mixing :has() and :not() is crazy powerful.
/* style articles without images differently */
article:not(:has(img)) {
padding-left: 2rem;
border-left: 4px solid #e5e7eb;
}
wait, does it have performance issues? okeyy, so
:has()evaluates from the subject to the argument. this can be expensive. try to scope it to a specific container instead of checking the wholebody.
modern browsers are super optimized, but simple selectors always win.
browser support
as of late 2025, :has() works in all major browsers. chrome, firefox, safari, edge - you name it.
for older browsers, you can write a fallback:
@supports selector(:has(*)) {
.card:has(img) {
padding: 0;
}
}
to sum it up
so yeah, :has() is the parent selector we always wanted. use it for state-based styling, combine it with :not(), and keep things simple for performance.
start using it today. your future self will thank you. lol.
building modern web apps? check out my guides on next.js server actions and why i recommend shadcn.