:has() Pseudo-class
This humble looking new pseudo-class might just be the most powerful pound-for-pound feature CSS has dropped in years.
It's base functionality is simple - select an element that contains something else, but this utility opens up so many doors and allows so many things to be accomplished in CSS where you would have previously had to reach for JS.
div:has(p) {
background: red;
}
<div class="has-[p]:bg-red"></div>
The most obvious use case for :has()
is of course targeting an element's container.
<label>
Option One
<input type="radio" />
</label>
label:has(:checked) {
/* Styles */
}
<label className="has-[:checked]:text-indigo-800 has-[:checked]:bg-indigo-50 ...">
Option Two
<input type="radio" />
</label>
We've been able to select adjacent siblings that follow an element for a long time now with the +
and ~
combinators, but we can now use these to select adjacent siblings that come before an element!
We can now easily select elements that come BEFORE another element!
(And we can still easily select elements that come after too of course!)
/* Target p after an img */
img + p { font-weight: bold; }
/* Target p before an img */
p:has(+ img) { font-weight: bold; }
/* Target p after an img */
<img class="[&+p]:font-bold" />
/* Target p before an img */
<p class="has-[+img]:font-bold"></p>
The :has
pseudo-class isn't limited to direct children - you can use it as high up the tree as you want, to the extent that we can use it as a kind of global detector to style something based on the existence or state of something else.
Another common use-case for me is to disable overflow on the body
tag if there is a modal or off-canvas menu currently active.
Go ahead and click the menu button in the bottom left of this page, and you will see the body can no longer be scrolled - without any JavaScript!
body:has(.sidebar) .any-element-on-page {
/* styles */
}
body:has(.slideout-menu-active) {
overflow: hidden;
}
<body class="has-[.slideout-menu-active]:overflow-hidden">
The possibilities are endless with :has()
- you could feasibly use it to create any number of interactive components, but remember, in many cases JS will be the more appopriate tool for the job!
I'm some content for tab one.
I'm some content for tab two.
I'm some content for tab three.
.tabs:has(.tab1:checked) .tab-panel1,
.tabs:has(.tab2:checked) .tab-panel2,
.tabs:has(.tab3:checked) .tab-panel3 {
display: block;
}
:has() Pseudo-class
Menu