Tailwind Group and Peer
Hover and focus classes normally affect the element you touch. Real designs often need a different result: pointing at a card should change the title inside it, or checking a box should change the label beside it. Group and peer variants solve both cases without any JavaScript.
The Group Variant
Add the group class to a parent element. Children can then react to the state of that parent with variants such as group-hover: and group-focus:. Think of a parent calling a family meeting: when the parent speaks, every child responds.
<a href="#" class="group block rounded-lg border p-4">
<h4 class="font-bold group-hover:text-sky-600">Card Title</h4>
<span class="opacity-0 group-hover:opacity-100">→</span>
</a>
Mouse away Mouse over the card
┌──────────────────┐ ┌──────────────────┐
│ Card Title │ → │ Card Title → │
└──────────────────┘ └──────────────────┘
title black, no arrow title blue, arrow visible
Pointing anywhere on the card triggers the change, even though the title and arrow never received the mouse themselves.
Named Groups
Nested groups create confusion because every child reacts to the closest group. Give each group a name with a slash and target that name in the variant.
<div class="group/card">
<div class="group/item">
<p class="group-hover/card:underline group-hover/item:font-bold">Text</p>
</div>
</div>
The text underlines when the outer card is hovered and turns bold when the inner item is hovered. Names remove all guesswork.
Group State Variants
Variant Reacts when the parent group is...
───────────────────────────────────────────────────────
group-hover: hovered
group-focus: focused
group-active: pressed
group-focus-within: holding a focused child
group-disabled: disabled
group-has-[...]: containing a matching element
The Peer Variant
The peer class works sideways. Mark one element as a peer, and its siblings that come after it can react to its state with peer-checked:, peer-focus:, or peer-invalid:. The reaction only travels forward, so the peer must appear earlier in the markup.
Markup order matters
<input class="peer"> ← the peer (first)
<label class="peer-checked:..."> ← reacts (comes after)
Reverse order does NOT work.
Custom Toggle Switch Example
A hidden checkbox drives the whole switch. The track and the knob respond to the checked state.
<label class="relative inline-flex cursor-pointer items-center">
<input type="checkbox" class="peer sr-only">
<div class="h-6 w-11 rounded-full bg-gray-300 peer-checked:bg-sky-600"></div>
<div class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5"></div>
</label>
Off: (● ) gray track
On: ( ●) blue track, knob slides right
Screen readers still announce the checkbox because the sr-only class hides it visually while keeping it in the page structure.
Form Validation Message Example
Show an error message only when the typed value fails the browser check. An email field without the required attribute counts as valid while empty, so the message stays hidden on a fresh form.
<input type="email" placeholder="you@example.com" class="peer border p-2">
<p class="hidden text-sm text-red-600 peer-invalid:block">
Enter a valid email address.
</p>
Empty field → no message
you@ → message appears
you@example.com → message disappears
The message follows the field state in real time. Visitors receive instant feedback without a single line of script.
Group vs Peer Quick Guide
Need Use
──────────────────────────────────────────────
Child reacts to its parent group
Sibling reacts to an earlier sibling peer
Several children react together group
Label reacts to a checkbox peer
Why Not Use JavaScript
Scripts can toggle classes on hover or click, but each script adds code to maintain, a delay while the page loads, and a chance for bugs. CSS state variants run inside the browser engine, which handles them instantly. The browser tracks details that scripts often forget, such as keyboard focus, touch screens, and disabled controls. Choosing group and peer variants gives faster pages and fewer defects.
Picking the Right Parent
Choose the group parent with care. A group on the whole page means every hover anywhere triggers children everywhere. A group on one card limits the effect to that card. Smaller groups give clearer results.
The same rule applies to peers. Wrap related controls in a small container, then place the peer and its dependent elements next to each other inside it. Long distances between the peer and the reacting element signal that a different structure, such as a parent group, might fit better.
Accessibility Notes
Hover effects help mouse users only. Touch screens and keyboards need an equal path. Pair every group-hover: class with group-focus-within: so a keyboard user who tabs into the card sees the same change. Hidden checkboxes need the sr-only class instead of hidden, because hidden removes the control from the keyboard order completely.
Good Habits
- Keep group parents small so a hover area stays predictable.
- Name groups whenever you nest them.
- Place the peer element before the elements that depend on it.
- Test keyboard focus along with mouse hover so every visitor gets the same feedback.
These two variants replace many small scripts. State-based styling stays in the markup, and the page remains fast.
