Mastering group and peer

Style an element based on the state of a parent (group) or a sibling (peer).

A utility normally reacts to its own state. group and peer break that limit so a child can react to its parent, and an element can react to a sibling.

group β€” react to an ancestor

Add group to a parent, then group-hover:, group-focus:, or group-data-[open]: to any descendant. Hover the card once and everything inside can respond together.

<a class="group">
  <h3 class="group-hover:text-cyan-600">Title</h3>
  <span class="opacity-0 group-hover:opacity-100">→</span>
</a>

peer β€” react to a previous sibling

Mark an element peer; a later sibling can then use peer-checked:, peer-focus:, or peer-invalid:. This is how you build floating labels and custom checkboxes with zero JavaScript.

Named groups and peers

Nesting two groups? Give them names so they do not collide: group/item on the parent and group-hover/item: on the child target that specific group.

Order matters for peer: the utility only sees siblings that come before it in the DOM. Put the input first, the label after.

Example

Try it yourself
Loading editor…
Press Run to see the result.

When to use it

  • A file list row highlights its delete button only when the row is hovered by using group on the li and group-hover:visible on the button.
  • A floating label input moves its label upward when the input is focused by using peer on the input and peer-focus:-translate-y-5 on the label.
  • A complex navigation uses named groups (group/nav) to scope group-hover effects so nested group elements do not interfere with each other.

More examples

Show action button on row hover

invisible hides the delete button from layout and interaction; group-hover:visible reveals it only when the parent row is hovered.

Example Β· html
<li class="group flex items-center justify-between p-3 hover:bg-gray-50 rounded">
  <span class="text-gray-800">document.pdf</span>
  <button
    class="invisible group-hover:visible text-sm text-red-500 hover:text-red-700"
  >
    Delete
  </button>
</li>

Floating label with peer

peer on the input exposes focus and content states; the label transitions upward via peer-focus: and stays up while text is present.

Example Β· html
<div class="relative">
  <input
    id="name"
    type="text"
    placeholder=" "
    class="peer w-full border-b-2 border-gray-300 px-0 py-2 text-gray-900
           focus:border-indigo-500 focus:outline-none"
  />
  <label
    for="name"
    class="absolute left-0 top-2 text-gray-500 text-sm
           transition-all duration-200
           peer-focus:-translate-y-6 peer-focus:text-xs peer-focus:text-indigo-600
           peer-[:not(:placeholder-shown)]:-translate-y-6
           peer-[:not(:placeholder-shown)]:text-xs"
  >
    Full Name
  </label>
</div>

Named groups for nested hover

Named groups (group/card and group/tag) allow independent hover scoping: card hover and tag hover each target only their own children.

Example Β· html
<div class="group/card bg-white border rounded-lg p-4 hover:border-indigo-300">
  <h3 class="font-semibold group-hover/card:text-indigo-600">Card Title</h3>
  <div class="group/tag mt-2 inline-flex">
    <span class="text-xs bg-gray-100 group-hover/tag:bg-indigo-100 px-2 py-1 rounded">
      Tag
    </span>
  </div>
</div>

Discussion

  • Be the first to comment on this lesson.
Mastering group and peer β€” Tailwind CSS | SoundsCode