iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

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…