Style a Parent Based on Its Children
The has-* Variant
The has-* variant applies styles to an element when it contains a matching descendant, the inverse of group.
What you'll learn
- Use has-[...] to react to descendant elements
- Combine has-* with group via group-has-*
- Build a selectable card that restyles when its input is checked
The has-* variant maps to the native CSS :has() selector. It styles an element based on what it contains — the opposite direction from group.
Reacting to Children
Pass a selector inside the brackets. The element is styled only when a matching descendant exists:
<figure class="p-4 has-[img]:p-0 has-[figcaption]:pb-3">
<img src="photo.jpg" alt="">
</figure> Here the padding collapses when the figure contains an image. has-[...] accepts any selector, including state selectors like has-[:focus] and has-[:checked].
The Selectable Card Pattern
The classic use is a card that highlights itself when the radio or checkbox inside it is selected:
<label class="block cursor-pointer rounded-lg border p-4
has-[:checked]:border-blue-500
has-[:checked]:bg-blue-50
has-[:checked]:ring-2 has-[:checked]:ring-blue-500">
<input type="radio" name="plan" class="sr-only">
<span class="font-medium">Pro plan</span>
</label> No JavaScript tracks the selection — the label restyles itself purely because it :has() a checked input.
Combining with group
group-has-* lets a descendant react when some other part of the group contains a match:
<article class="group rounded-xl border p-6">
<a href="#">Linked content</a>
<footer class="group-has-[a]:underline text-sm text-gray-500">
Has a link
</footer>
</article> This composability — has-*, group-has-*, and arbitrary selectors — replaces a surprising amount of state-tracking script with declarative CSS.