group & peer
group and peer in Tailwind: style a child based on its parent or sibling state, without leaving markup.
Tailwind — group + peer
EXAMPLE
<!-- ===== group: parent-driven ===== -->
<a href="/" class="group flex items-center gap-2 p-2 rounded hover:bg-slate-100">
<span class="h-5 w-5 rounded-full bg-slate-300 group-hover:bg-blue-600"></span>
<span class="text-slate-600 group-hover:text-blue-700">Home</span>
</a>
<!-- Hover the <a> -> the icon AND text change colour. -->
<!-- group-focus, group-active, group-disabled, group-aria-expanded -->
<button class="group" aria-expanded="false">
Toggle
<svg class="transition-transform group-aria-expanded:rotate-180">...</svg>
</button>
<!-- Named groups (when nesting): -->
<div class="group/parent">
<div class="group/child">
<p class="group-hover/parent:text-red-500 group-hover/child:text-blue-500">two scopes</p>
</div>
</div>
<!-- ===== peer: sibling-driven (only WITHIN same parent + peer FIRST) ===== -->
<div>
<input type="checkbox" class="peer sr-only" id="toggle" />
<label for="toggle" class="flex items-center cursor-pointer">
<span class="h-5 w-9 rounded-full bg-slate-300 peer-checked:bg-blue-600 transition"></span>
<span class="ml-2 text-slate-600 peer-checked:text-blue-700">Notifications</span>
</label>
</div>
<!-- peer-focus, peer-disabled, peer-invalid, peer-placeholder-shown -->
<!-- Floating label trick: -->
<div class="relative">
<input type="email" placeholder=" " class="peer block w-full border rounded px-3 pt-4 pb-1" />
<label class="absolute left-3 top-1 text-xs text-slate-500
peer-placeholder-shown:top-3 peer-placeholder-shown:text-base peer-placeholder-shown:text-slate-400
peer-focus:top-1 peer-focus:text-xs peer-focus:text-blue-600 transition-all">
Email
</label>
</div>
<!-- ===== Limitations =====
- 'peer' must come BEFORE the styled sibling in the DOM
- 'peer' and 'group' work within the SAME parent; cross-tree style needs JS
- Use named groups (group/foo, peer/foo) when nesting more than one
-->
<!-- ===== Patterns to internalise =====
- group + group-hover for icon + label hover patterns
- peer for form controls + accessible toggles + floating labels
- aria-* variants (group-aria-expanded, peer-aria-checked) to mirror state
- Named groups + peers to avoid bleeding across nesting
-->
<!-- ===== Pitfalls =====
- 'peer' AFTER the styled sibling -> doesn't work (CSS sibling selector only forwards)
- group-hover applied without 'group' on the ancestor -> nothing happens
- Floating label without 'placeholder= \" \"' fallback -> peer-placeholder-shown does not fire
- Using JS to toggle classes when peer/group + state attributes would have done
-->
Why it matters
group and peer let you express parent-driven and sibling-driven styles directly in markup. They make hover-everywhere navigation, floating labels, and accessible toggles look easy. Once they are reflex you stop reaching for JS for things CSS can already do.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
<div class="group">
<p class="group-hover:text-emerald-600">Hover the parent…</p>
</div>
Try it Yourself »
Discussion
Loading…