Tailwind CSS 35 🎨 Named Groups and Named Peers (group-/{name} and peer-/{name})
A group is a parent. A peer is a sibling. The group-hover: variant applies when the nearest group is hovered, and the peer-focus: variant applies when the nearest peer is focused. The word “nearest” is the problem. When groups are nested, or when a parent has multiple peers, the nearest one is not always the one the developer intended. The named forms—group/{name} and peer/{name}—disambiguate. They give each group and each peer a name, and the variant targets the name.
This chapter covers the named forms in full. You will learn the syntax, the nesting cases they solve, the multiple-peer cases they solve, and the patterns that use them for cards within cards, forms with multiple fields, and components that need to react to a specific ancestor or sibling.
Key point: A named group is declared with group/{name} on the parent and targeted with group-{variant}/{name}: on the child. A named peer is declared with peer/{name} on the sibling and targeted with peer-{variant}/{name}: on the later sibling. The name is the disambiguator. Without it, the variant targets the nearest group or peer, which may be the wrong one.
Why named groups and peers matter
The nesting problem. A page has an outer group and an inner group. A child inside the inner group uses group-hover:. Which group does it target? The nearest one—the inner group. If the intent is the outer group, the variant cannot reach it. The named form group-hover/outer: targets the outer group explicitly.
The multiple-peer problem. A form has an email input and a password input, both marked peer. A message below both uses peer-invalid:. Which peer does it target? The CSS uses the general sibling combinator, which matches any earlier sibling with the peer class. With two peers, the behavior depends on the DOM order and the specificity, and the result is not what the developer intended. The named form peer-invalid/email: targets the email peer specifically.
The readability problem. An unnamed group in a nested structure is ambiguous. The reader has to trace the DOM to find which parent the variant targets. The named form makes the target explicit. group-hover/card: is clear: it targets the group named card.
The component problem. A reusable component contains a group. The component is used inside another group. The component’s internal group-hover: targets the component’s own group, not the outer one. If the component should react to the outer group, the outer group needs a name and the component needs to target it.
The Tailwind v3.2 problem. The named forms were introduced in Tailwind v3.2. They are part of the group and peer system, and they compose with the existing variants. The names are arbitrary; they are the developer’s choice.
The composition problem. A child can target a named group and a named peer at the same time. group-hover/card:peer-focus/email:text-blue-600 applies when the card group is hovered and the email peer is focused. The two named targets are independent.
a. The named group syntax
A named group is declared with group/{name} on the parent.
<div class="group/card">
<p class="group-hover/card:text-blue-600">Title</p>
</div>
The group/card declares a group named card. The group-hover/card: targets that group. The name is card; it can be any string that is a valid CSS identifier.
The unnamed group and the named group can coexist.
<div class="group/outer">
<div class="group">
<p class="group-hover:text-blue-600">Inner hover</p>
<p class="group-hover/outer:text-red-600">Outer hover</p>
</div>
</div>
The inner <div> is an unnamed group. The group-hover: on the first <p> targets the nearest group, which is the inner one. The group-hover/outer: on the second <p> targets the outer group. The two variants are distinct.
The named group works with any state variant.
<div class="group/menu">
<ul class="hidden group-open/menu:block">...</ul>
</div>
The group-open/menu: applies when the group named menu is open. The <details> element with group/menu would be open when the details is expanded.
Multiple named groups can be nested.
<div class="group/card">
<div class="group/inner">
<p class="group-hover/card:text-blue-600">Card</p>
<p class="group-hover/inner:text-red-600">Inner</p>
</div>
</div>
The card group and the inner group are distinct. The group-hover/card: targets the card, and the group-hover/inner: targets the inner. The reader can see the target from the name.
The name can be a single word or a hyphenated identifier.
<div class="group/product-card">
<p class="group-hover/product-card:text-blue-600">Title</p>
</div>
The name product-card is a hyphenated identifier. The variant group-hover/product-card: targets it.
The name must be the same in the declaration and the variant. A typo in either produces a variant that targets nothing.
<div class="group/card">
<p class="group-hover/crd:text-blue-600">Title</p> <!-- typo -->
</div>
The crd does not match card. The variant has no effect. The typo is silent; there is no error, only the missing style.
b. The named peer syntax
A named peer is declared with peer/{name} on the sibling.
<input class="peer/email" type="email" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
The peer/email declares a peer named email. The peer-invalid/email: targets that peer. The name is email.
Multiple named peers can coexist.
<div>
<input class="peer/email" type="email" />
<input class="peer/password" type="password" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<p class="peer-invalid/password:text-red-500">Invalid password</p>
</div>
The email peer and the password peer are distinct. The peer-invalid/email: targets the email input, and the peer-invalid/password: targets the password input. The messages are independent.
The named peer must come before the target in the DOM.
<!-- CORRECT -->
<input class="peer/email" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<!-- WRONG: target before peer -->
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<input class="peer/email" />
The second example does not work because the target comes before the peer. The CSS selector cannot match an earlier sibling.
The named peer works with any state variant.
<input class="peer/terms" type="checkbox" />
<span class="peer-checked/terms:font-bold">I agree</span>
The peer-checked/terms: applies when the terms checkbox is checked.
The named peer and the unnamed peer can coexist.
<input class="peer/email" type="email" />
<input class="peer" type="password" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<p class="peer-invalid:text-red-500">Invalid password</p>
The first input is the email peer. The second is an unnamed peer. The peer-invalid/email: targets the email input. The peer-invalid: targets the nearest peer, which is the password input (because it is the later sibling before the <p>).
The name is the disambiguator. Without it, the variant targets the nearest peer, which may be the wrong one.
The named peer composes with the named group.
<div class="group/form">
<input class="peer/email" type="email" />
<p class="peer-invalid/email:group-hover/form:text-red-500">
Invalid email
</p>
</div>
The peer-invalid/email:group-hover/form: applies when the email peer is invalid and the form group is hovered. The two named targets compose.
c. The patterns
Card within a card.
<div class="group/outer">
<div class="group/inner rounded border p-4 hover:shadow-lg">
<h3 class="group-hover/inner:text-blue-600">Inner title</h3>
<p class="group-hover/outer:bg-gray-50">Outer hover effect</p>
</div>
</div>
The inner card’s title changes when the inner card is hovered. The paragraph’s background changes when the outer group is hovered. The two targets are distinct.
Form with multiple fields.
<div>
<input class="peer/email" type="email" required />
<p class="peer-invalid/email:block hidden text-red-500">Invalid email</p>
<input class="peer/password" type="password" required minlength="8" />
<p class="peer-invalid/password:block hidden text-red-500">Password too short</p>
</div>
Each field has its own error message, and each message is shown only when its own field is invalid.
Floating label with a name.
<div class="relative">
<input class="peer/email placeholder-transparent" placeholder="Email" />
<label class="peer-placeholder-shown/email:top-2 peer-focus/email:-top-3">
Email
</label>
</div>
The label targets the email peer specifically. If the form has another peer, the label is not confused by it.
Navigation with nested groups.
<nav class="group/nav">
<ul>
<li class="group/item">
<a class="group-hover/item:text-blue-600 group-hover/nav:bg-gray-50">Link</a>
</li>
</ul>
</nav>
The link changes color when its item is hovered and the nav’s background changes when the nav is hovered. The two named groups are independent.
Menu with a named group.
<details class="group/menu">
<summary class="cursor-pointer">Menu</summary>
<ul class="hidden group-open/menu:block">
<li>Item 1</li>
<li>Item 2</li>
</ul>
</details>
The group-open/menu: shows the list when the details is open. The name menu targets the specific group.
Toggle switch with a named peer.
<label class="flex items-center">
<input type="checkbox" class="peer/toggle sr-only" />
<div class="w-10 h-5 bg-gray-300 rounded-full peer-checked/toggle:bg-blue-600">
<div class="w-4 h-4 bg-white rounded-full transition-transform peer-checked/toggle:translate-x-5" />
</div>
</label>
The toggle’s background and knob react to the toggle peer. The name disambiguates from any other peer in the form.
Validation with a named group and peer.
<div class="group/form">
<input class="peer/email" type="email" required />
<p class="peer-invalid/email:text-red-500 group-hover/form:font-bold">
Please enter a valid email
</p>
</div>
The message turns red when the email is invalid and bold when the form is hovered. The two named targets compose.
Nested forms.
<form class="group/login">
<input class="peer/user" />
<input class="peer/pass" type="password" />
<p class="peer-invalid/user:group-hover/login:text-red-500">
Check your credentials
</p>
</form>
The message reacts to the user peer and the login group. The names disambiguate.
Tabs with a named group.
<div class="group/tabs">
<button class="group-hover/tabs:bg-gray-100">Tab 1</button>
<button class="group-hover/tabs:bg-gray-100">Tab 2</button>
</div>
The tabs change background when the container is hovered.
Tooltip with a named group.
<div class="group/tooltip relative">
<button>Hover</button>
<div class="absolute hidden group-hover/tooltip:block">Tooltip</div>
</div>
The tooltip is shown when the tooltip group is hovered.
Complete Example Session
<!-- ============================================ -->
<!-- PART 1: BASIC NAMED GROUP -->
<!-- ============================================ -->
<div class="group/card">
<p class="group-hover/card:text-blue-600">Title</p>
</div>
<!-- ============================================ -->
<!-- PART 2: NESTED NAMED GROUPS -->
<!-- ============================================ -->
<div class="group/outer">
<div class="group/inner">
<p class="group-hover/inner:text-blue-600">Inner</p>
<p class="group-hover/outer:text-red-600">Outer</p>
</div>
</div>
<!-- ============================================ -->
<!-- PART 3: BASIC NAMED PEER -->
<!-- ============================================ -->
<input class="peer/email" type="email" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<!-- ============================================ -->
<!-- PART 4: MULTIPLE NAMED PEERS -->
<!-- ============================================ -->
<div>
<input class="peer/email" type="email" />
<input class="peer/password" type="password" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<p class="peer-invalid/password:text-red-500">Invalid password</p>
</div>
<!-- ============================================ -->
<!-- PART 5: NAMED GROUP IN NESTED CARDS -->
<!-- ============================================ -->
<div class="group/outer">
<div class="group/inner rounded border p-4">
<h3 class="group-hover/inner:text-blue-600">Inner title</h3>
<p class="group-hover/outer:bg-gray-50">Outer effect</p>
</div>
</div>
<!-- ============================================ -->
<!-- PART 6: NAMED PEER FOR FLOATING LABEL -->
<!-- ============================================ -->
<div class="relative">
<input class="peer/email placeholder-transparent" placeholder="Email" />
<label class="peer-placeholder-shown/email:top-2 peer-focus/email:-top-3">
Email
</label>
</div>
<!-- ============================================ -->
<!-- PART 7: NAMED GROUP FOR DROPDOWN -->
<!-- ============================================ -->
<details class="group/menu">
<summary>Menu</summary>
<ul class="hidden group-open/menu:block">
<li>Item 1</li>
</ul>
</details>
<!-- ============================================ -->
<!-- PART 8: NAMED PEER FOR TOGGLE -->
<!-- ============================================ -->
<label class="flex items-center">
<input type="checkbox" class="peer/toggle sr-only" />
<div class="peer-checked/toggle:bg-blue-600">
<div class="peer-checked/toggle:translate-x-5" />
</div>
</label>
<!-- ============================================ -->
<!-- PART 9: NAMED GROUP AND PEER COMPOSED -->
<!-- ============================================ -->
<div class="group/form">
<input class="peer/email" type="email" required />
<p class="peer-invalid/email:group-hover/form:font-bold">
Check your email
</p>
</div>
<!-- ============================================ -->
<!-- PART 10: NAMED GROUP FOR NAVIGATION -->
<!-- ============================================ -->
<nav class="group/nav">
<ul>
<li class="group/item">
<a class="group-hover/item:text-blue-600 group-hover/nav:bg-gray-50">
Link
</a>
</li>
</ul>
</nav>
The ten parts covered the basic named group, nested named groups, the basic named peer, multiple named peers, named groups in nested cards, the named peer for the floating label, the named group for the dropdown, the named peer for the toggle, the named group and peer composed, and the named group for navigation.
Quick Reference
Named Group Syntax
| Part | Syntax |
|---|---|
| Declare | group/{name} |
| Target | group-{variant}/{name}: |
| Example declare | group/card |
| Example target | group-hover/card: |
Named Peer Syntax
| Part | Syntax |
|---|---|
| Declare | peer/{name} |
| Target | peer-{variant}/{name}: |
| Example declare | peer/email |
| Example target | peer-invalid/email: |
Common Named Groups
| Declaration | Target |
|---|---|
group/card | group-hover/card: |
group/menu | group-open/menu: |
group/nav | group-hover/nav: |
group/form | group-hover/form: |
group/outer | group-hover/outer: |
Common Named Peers
| Declaration | Target |
|---|---|
peer/email | peer-invalid/email: |
peer/password | peer-invalid/password: |
peer/toggle | peer-checked/toggle: |
peer/terms | peer-checked/terms: |
peer/email | peer-focus/email: |
Rules
| Rule | Requirement |
|---|---|
| Name match | Declaration and target must match |
| Peer order | Peer before target |
| Same parent | Group/peer and target share a parent |
| Composition | Named group and peer combine |
| Typo | Silent failure |
Best Practices
✅ Do This:
<!-- Name the group when nested -->
<div class="group/outer">
<div class="group/inner">
<p class="group-hover/outer:text-red-600">Outer</p>
</div>
</div> <!-- ✅ -->
<!-- Name the peer when multiple -->
<input class="peer/email" />
<input class="peer/password" />
<p class="peer-invalid/email:text-red-500">Email error</p> <!-- ✅ -->
<!-- Use consistent names -->
<div class="group/card">
<p class="group-hover/card:text-blue-600">Title</p>
</div> <!-- ✅ -->
<!-- Compose named group and peer -->
<p class="peer-invalid/email:group-hover/form:font-bold"> <!-- ✅ -->
</p>
<!-- Use the name in the declaration and the target -->
<input class="peer/terms" />
<span class="peer-checked/terms:font-bold">I agree</span> <!-- ✅ -->
<!-- Keep names descriptive -->
<div class="group/product-card"> <!-- ✅ -->
</div>
❌ Don’t Do This:
<!-- Don't mismatch the names -->
<div class="group/card">
<p class="group-hover/crd:text-blue-600"> <!-- typo --> <!-- ❌ -->
</p>
</div>
<!-- Don't use a named peer without the declaration -->
<input class="peer" />
<p class="peer-invalid/email:text-red-500"> <!-- no peer/email --> <!-- ❌ -->
</p>
<!-- Don't put the peer after the target -->
<p class="peer-invalid/email:text-red-500">Email error</p>
<input class="peer/email" /> <!-- wrong order --> <!-- ❌ -->
<!-- Don't use names that collide with other classes -->
<div class="group/flex"> <!-- confusing --> <!-- ⚠️ -->
<!-- Don't use a name that is not a valid identifier -->
<div class="group/my card"> <!-- space in name --> <!-- ❌ -->
<!-- Don't forget the name in the target -->
<div class="group/card">
<p class="group-hover:text-blue-600"> <!-- unnamed --> <!-- ⚠️ -->
</p>
</div>
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Variant no effect | Name mismatch | Match the names |
| Wrong group targeted | No name | Use the named form |
| Wrong peer targeted | Multiple peers | Use the named form |
| Peer after target | DOM order | Move peer before |
| Typo in the name | Silent failure | Check the spelling |
| Name not a valid identifier | Space or special character | Use a valid name |
| Named peer without declaration | Missing peer/name | Add the declaration |
Real-World Examples
1. Nested Cards
<div class="group/outer">
<div class="group/inner">
<h3 class="group-hover/inner:text-blue-600">Title</h3>
<p class="group-hover/outer:bg-gray-50">Body</p>
</div>
</div>
2. Multiple Form Fields
<input class="peer/email" type="email" />
<p class="peer-invalid/email:text-red-500">Invalid email</p>
<input class="peer/password" type="password" />
<p class="peer-invalid/password:text-red-500">Invalid password</p>
3. Named Floating Label
<input class="peer/email placeholder-transparent" />
<label class="peer-focus/email:-top-3">Email</label>
4. Named Dropdown
<details class="group/menu">
<ul class="hidden group-open/menu:block">...</ul>
</details>
5. Named Toggle
<input class="peer/toggle sr-only" type="checkbox" />
<div class="peer-checked/toggle:bg-blue-600">...</div>
6. Named Navigation
<nav class="group/nav">
<a class="group-hover/nav:bg-gray-50">Link</a>
</nav>
7. Composed Named Targets
<p class="peer-invalid/email:group-hover/form:font-bold">Error</p>
8. Named Card
<div class="group/card">
<h3 class="group-hover/card:text-blue-600">Title</h3>
</div>
9. Named Terms Checkbox
<input class="peer/terms" type="checkbox" />
<span class="peer-checked/terms:font-bold">I agree</span>
10. Named Tooltip
<div class="group/tooltip">
<div class="hidden group-hover/tooltip:block">Tooltip</div>
</div>
Visual
Named Group in Nested Structure
┌─────────────────────────────────────────────────────────────┐
│ <div class="group/outer"> │
│ <div class="group/inner"> │
│ <p class="group-hover/inner:text-blue-600">Inner</p> │
│ <p class="group-hover/outer:text-red-600">Outer</p> │
│ </div> │
│ </div> │
│ │
│ Hover the inner: │
│ - "Inner" turns blue (inner is hovered) │
│ - "Outer" turns red (outer is also hovered) │
│ │
│ Hover the outer (not inner): │
│ - "Inner" stays default │
│ - "Outer" turns red │
│ │
│ The names disambiguate the target. │
│ │
└─────────────────────────────────────────────────────────────┘
Multiple Named Peers
┌─────────────────────────────────────────────────────────────┐
│ <input class="peer/email" /> │
│ <input class="peer/password" /> │
│ <p class="peer-invalid/email:text-red-500">Email</p> │
│ <p class="peer-invalid/password:text-red-500">Password</p> │
│ │
│ Email invalid, password valid: │
│ - "Email" is red (email is invalid) │
│ - "Password" is default (password is valid) │
│ │
│ Password invalid, email valid: │
│ - "Email" is default │
│ - "Password" is red │
│ │
│ Each message targets its own peer. │
│ │
└─────────────────────────────────────────────────────────────┘
Named Peer with Unnamed Peer
┌─────────────────────────────────────────────────────────────┐
│ <input class="peer/email" /> │
│ <input class="peer" /> │
│ <p class="peer-invalid/email:text-red-500">Email</p> │
│ <p class="peer-invalid:text-red-500">Nearest peer</p> │
│ │
│ The named target: the email peer. │
│ The unnamed target: the nearest peer. │
│ │
│ The nearest peer is the second input (the unnamed one), │
│ because it is the latest peer before the <p>. │
│ │
│ The names disambiguate. │
│ │
└─────────────────────────────────────────────────────────────┘
Naming Rules
┌─────────────────────────────────────────────────────────────┐
│ DECLARATION TARGET │
│ │
│ group/card ──▶ group-hover/card: │
│ peer/email ──▶ peer-invalid/email: │
│ │
│ The name must match. │
│ A typo produces a silent failure. │
│ The name must be a valid CSS identifier. │
│ The peer must come before the target. │
│ │
└─────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| Named group declare | group/{name} |
| Named group target | group-{variant}/{name}: |
| Named peer declare | peer/{name} |
| Named peer target | peer-{variant}/{name}: |
| Name match | Required |
| Peer order | Peer before target |
| Composition | Named group + named peer |
| Typo | Silent failure |
| Version | Tailwind v3.2+ |
Key takeaways:
- The named forms disambiguate the target. The
group-hover:targets the nearest group. Thegroup-hover/card:targets the group namedcard. The name removes the ambiguity. - The named group is the tool for nested groups. When an outer group and an inner group coexist, the child can target either one by name. Without the name, the variant always targets the nearest group.
- The named peer is the tool for multiple peers. When a parent has more than one peer, the child can target a specific one by name. Without the name, the variant targets the nearest peer, which may be the wrong one.
- The name must match in the declaration and the target. A typo produces a variant that targets nothing. The failure is silent; there is no error, only the missing style.
- The peer must come before the target in the DOM. The named form does not change the ordering rule. The peer is the earlier sibling; the target is the later one.
- The named forms compose. A child can target a named group and a named peer at the same time.
peer-invalid/email:group-hover/form:applies when the email peer is invalid and the form group is hovered. - The names are arbitrary. They are the developer’s choice. Use descriptive names that match the component or the field.
group/card,peer/email,group/menu,peer/toggle.
Remember: The named forms are the disambiguators. When there is only one group or one peer, the unnamed form is enough. When there are several, the named form is the only way to target the right one. The name is the contract between the declaration and the target, and the contract must be exact. Use the named forms for the nested cards, the multi-field forms, the navigation with items, and the components within components. The names make the intent visible, and the intent is the target.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!