Skip to main content

Wait, What's [&_button] in Tailwind? Understanding &, >, and _ Selectors for Nested Elements

Β· 12 min read
Mahmut Salman
Software Developer

I saw this weird Tailwind class: [&_button]:rounded-lg and had no idea what the & and _ meant! πŸ€” It looked like CSS, but also different? My mentor explained these are arbitrary variant selectorsβ€”Tailwind's way of targeting child elements. The _ (underscore) represents a space in CSS selectors, and it's the key to styling deeply nested children. This conversation unlocked a whole new level of Tailwind power!

The Mysterious [&_button] Syntax​

Me (Backend Dev): I'm looking at this Tailwind code:

<div className="[&_button]:rounded-lg [&_button]:px-4 [&_button]:py-2">
<button>Fill User</button>
<div className="flex gap-3">
<button>Fill Admin</button>
</div>
</div>

Both buttons get the styling, even though one is nested inside another div! What is [&_button]? It looks like CSS but I've never seen & and _ used like this in Tailwind.

Understanding Tailwind Arbitrary Variants​

Frontend Mentor: Great question! You've discovered Tailwind's arbitrary variant syntaxβ€”a powerful feature for targeting child elements. Let me break down these special symbols:

The Three Key Symbols​

  1. & β†’ "The current element itself" (parent reference)
  2. > β†’ "Only my direct children"
  3. _ β†’ "All descendants at any level (space in CSS)"

Think of these as Tailwind's way of writing CSS selectors!

Symbol 1: & (Parent Reference)​

Frontend Mentor: The & symbol refers to the current element itself.

Basic Example​

<div className="[&]:hover:bg-red-500">
Hover me
</div>

What it means:

  • & = "this div itself"
  • Same as writing regular hover:bg-red-500

CSS equivalent:

div:hover {
background-color: rgb(239 68 68);
}

Me: So why use & if it's the same as regular classes?

Frontend Mentor: Good question! & is more useful in complex selectors:

Advanced Example​

<div className="group [&:hover]:scale-110 [&:focus-within]:ring-2">
Complex interactions
</div>

Why use &:

  • Combining multiple pseudo-classes
  • Creating custom arbitrary variants
  • More explicit when writing complex selectors
  • Required for child selectors (which we'll see next)

Symbol 2: > (Direct Children Only)​

Frontend Mentor: The > symbol targets only immediate children.

Example: Styling Direct Children Only​

<div className="[&>button]:rounded-lg [&>button]:bg-blue-500">
<button>I get styled βœ…</button>
<button>I also get styled βœ…</button>
<div>
<button>I DON'T get styled ❌</button>
</div>
</div>

What happens:

  • First two buttons are direct children β†’ βœ… Styled
  • Third button is nested inside div β†’ ❌ NOT styled

Visual representation:

β”Œβ”€ Parent div ──────────────────────┐
β”‚ [button] ← Direct child βœ… β”‚
β”‚ [button] ← Direct child βœ… β”‚
β”‚ β”Œβ”€ Nested div ─────────────────┐ β”‚
β”‚ β”‚ [button] ← NOT direct ❌ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CSS equivalent:

div > button {
border-radius: 0.5rem;
background-color: rgb(59 130 246);
}

When to Use >​

Use direct children selector (>) when:

  • βœ… You want to style only the immediate children
  • βœ… You have nested structures and want to avoid cascade
  • βœ… You need precise control over which level gets styled

Example use case:

{/* Style only top-level list items */}
<ul className="[&>li]:font-bold">
<li>Bold βœ…</li>
<li>Bold βœ…
<ul>
<li>NOT bold ❌</li>
</ul>
</li>
</ul>

Symbol 3: _ (All Descendants)​

Frontend Mentor: The _ (underscore) represents a space in CSS selectors, targeting all descendants at any level!

Example: Styling All Nested Children​

<div className="[&_button]:rounded-lg [&_button]:bg-blue-500">
<button>I get styled βœ…</button>
<div>
<button>I ALSO get styled βœ…</button>
</div>
<div>
<div>
<div>
<button>Even me! βœ…</button>
</div>
</div>
</div>
</div>

What happens:

  • ALL buttons at ANY nesting level get styled! βœ…

Visual representation:

β”Œβ”€ Parent div ──────────────────────┐
β”‚ [button] ← Styled βœ… β”‚
β”‚ β”Œβ”€ Nested div ─────────────────┐ β”‚
β”‚ β”‚ [button] ← Styled βœ… β”‚ β”‚
β”‚ β”‚ β”Œβ”€ Even deeper ────────────┐ β”‚ β”‚
β”‚ β”‚ β”‚ [button] ← Styled βœ… β”‚ β”‚ β”‚
β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

CSS equivalent:

div button {  /* Space = any descendant */
border-radius: 0.5rem;
background-color: rgb(59 130 246);
}

Why Underscore _ Instead of Space?​

Me: Why use _ instead of just a space?

Frontend Mentor: Because Tailwind classes can't have spaces! The _ is Tailwind's way of representing a space in arbitrary selectors:

❌ This won't work (space breaks the class):
<div className="[& button]:rounded-lg">

βœ… This works (underscore = space):
<div className="[&_button]:rounded-lg">

The translation:

  • Tailwind: [&_button]
  • CSS: & button (with actual space)

Quick Comparison Table​

Me: Can you show me all three side-by-side?

Frontend Mentor: Absolutely! Here's the complete comparison:

SelectorTailwind SyntaxCSS EquivalentTargetsExample
Self[&]:hover&:hoverThe element itselfHover states, focus states
Direct children[&>button]& > buttonOnly immediate childrenTop-level buttons only
All descendants[&_button]& buttonAll nested childrenButtons at ANY nesting level

Visual Example: All Three Together​

<div className="border p-4 [&]:hover:bg-gray-100 [&>p]:font-bold [&_span]:text-red-500">
{/* [&]:hover:bg-gray-100 - Self: hover on this div */}

<p>Direct child paragraph (bold) βœ…
<span>Nested span (red) βœ…</span>
</p>

{/* [&>p]:font-bold - Direct children: only this p is bold */}

<div>
<p>Nested paragraph (NOT bold) ❌
<span>Deeply nested span (red) βœ…</span>
</p>
</div>

{/* [&_span]:text-red-500 - All descendants: ALL spans are red */}
</div>

Result:

β”Œβ”€ Div (hover for gray background) ────────────┐
β”‚ β”‚
β”‚ Direct child paragraph (bold) βœ… β”‚
β”‚ Nested span (red) βœ… β”‚
β”‚ β”‚
β”‚ β”Œβ”€ Nested div ────────────────────────────┐ β”‚
β”‚ β”‚ Nested paragraph (NOT bold) ❌ β”‚ β”‚
β”‚ β”‚ Deeply nested span (red) βœ… β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Your Original Case Explained​

Me: So in my code, why did I need [&_button] instead of [&>button]?

Frontend Mentor: Excellent question! Let's examine your structure:

Your Code​

<div className="[&_button]:rounded-lg [&_button]:px-4 [&_button]:py-2">
<button>Fill User</button>
<div className="flex gap-3">
<button>Fill Admin</button>
</div>
</div>

Why [&_button] Works​

Structure:

β”Œβ”€ Parent div ──────────────────────┐
β”‚ <button>Fill User</button> β”‚ ← Direct child βœ…
β”‚ <div class="flex gap-3"> β”‚
β”‚ <button>Fill Admin</button> β”‚ ← Nested child βœ…
β”‚ </div> β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

With [&_button] (all descendants):

  • "Fill User" button β†’ βœ… Styled (direct child)
  • "Fill Admin" button β†’ βœ… Styled (nested inside flex div)

Why [&>button] Would Fail​

<div className="[&>button]:rounded-lg [&>button]:px-4 [&>button]:py-2">
<button>Fill User</button>
<div className="flex gap-3">
<button>Fill Admin</button>
</div>
</div>

With [&>button] (direct children only):

  • "Fill User" button β†’ βœ… Styled (direct child)
  • "Fill Admin" button β†’ ❌ NOT styled (not a direct child!)

Visual:

β”Œβ”€ Parent div ──────────────────────┐
β”‚ [button] ← Direct βœ… β”‚
β”‚ β”Œβ”€ flex div ──────────────────┐ β”‚
β”‚ β”‚ [button] ← Nested ❌ β”‚ β”‚ ← Blocked by >
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The key insight:

  • The _ (space) lets styles "penetrate" through the <div class="flex gap-3"> wrapper!
  • The > would be "blocked" by the wrapper div

Real-World Use Cases​

Me: When would I use each selector in practice?

Frontend Mentor: Great question! Here are practical examples:

Use Case 1: Style All Buttons in a Form (Use _)​

<form className="[&_button]:px-4 [&_button]:py-2 [&_button]:rounded-lg [&_button]:bg-blue-600 [&_button]:text-white">
<div className="mb-4">
<label>Email</label>
<input type="email" />
</div>

<div className="flex gap-2">
<button type="submit">Submit</button>
<button type="button">Cancel</button>
</div>

<div className="mt-4">
<div className="flex justify-end">
<button type="button">Reset</button>
</div>
</div>
</form>

Why [&_button]:

  • Buttons are at different nesting levels
  • You want consistent styling for ALL buttons
  • Don't want to add classes to each button individually

Use Case 2: Style Direct Children Only (Use >)​

<nav className="[&>a]:px-4 [&>a]:py-2 [&>a]:text-gray-700">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>

{/* Dropdown shouldn't get nav styles */}
<div className="dropdown">
<a href="/profile">Profile</a> {/* NOT styled βœ… */}
<a href="/settings">Settings</a> {/* NOT styled βœ… */}
</div>
</nav>

Why [&>a]:

  • Only top-level nav links should get nav styling
  • Dropdown links are nested and should have different styles
  • Prevents style cascade into nested structures

Use Case 3: Style All Input Fields (Use _)​

<div className="[&_input]:border [&_input]:border-gray-300 [&_input]:rounded-md [&_input]:px-3 [&_input]:py-2 [&_input]:w-full">
<div className="mb-4">
<label>First Name</label>
<input type="text" />
</div>

<div className="mb-4">
<label>Email</label>
<input type="email" />
</div>

<div className="grid grid-cols-2 gap-4">
<div>
<label>City</label>
<input type="text" />
</div>
<div>
<label>Zip</label>
<input type="text" />
</div>
</div>
</div>

Why [&_input]:

  • Inputs are in various nested structures
  • Consistent styling across all inputs
  • Write once, apply everywhere

Use Case 4: Hover Effects on Parent (Use &)​

<div className="group [&:hover]:bg-gray-100 [&:hover]:shadow-lg transition-all">
<h3>Product Card</h3>
<p>Hover me to see the effect</p>
</div>

Why [&:hover]:

  • Targeting the element itself
  • Creating hover states
  • Can be combined with other pseudo-classes

Combining Multiple Selectors​

Me: Can I use multiple selectors together?

Frontend Mentor: Absolutely! You can combine them for powerful targeting:

Example 1: Different Styles for Different Elements​

<div className="[&_button]:bg-blue-500 [&_input]:border-gray-300 [&_label]:font-semibold">
<div>
<label>Name</label>
<input type="text" />
</div>

<div className="flex gap-2">
<button>Save</button>
<button>Cancel</button>
</div>
</div>

What happens:

  • All buttons β†’ Blue background
  • All inputs β†’ Gray border
  • All labels β†’ Semibold font

Example 2: Direct vs Nested​

<div className="[&>button]:bg-blue-500 [&_span]:text-red-500">
<button>Direct button (blue) βœ…</button>
<div>
<button>Nested button (NOT blue) ❌
<span>But span is red βœ…</span>
</button>
</div>
</div>

Key insight:

  • [&>button] β†’ Only direct children
  • [&_span] β†’ All descendants (including inside nested button)

Example 3: Multiple Conditions on Same Selector​

<div className="[&_button]:px-4 [&_button]:py-2 [&_button]:rounded-lg [&_button]:bg-blue-600 [&_button]:hover:bg-blue-700">
<button>Hover me</button>
</div>

Or more readable:

<div className="
[&_button]:px-4
[&_button]:py-2
[&_button]:rounded-lg
[&_button]:bg-blue-600
[&_button]:hover:bg-blue-700
">
<button>Hover me</button>
</div>

Advanced: Combining with Pseudo-classes​

Me: Can I add hover, focus, etc. to these selectors?

Frontend Mentor: Yes! You can chain pseudo-classes:

Example: Hover States on Children​

<div className="[&_button:hover]:bg-blue-700 [&_button:focus]:ring-2">
<button>Hover me</button>
<div>
<button>Or hover me</button>
</div>
</div>

What it does:

  • All buttons get blue background on hover
  • All buttons get ring on focus
  • Works for buttons at any nesting level

Example: Different States​

<div className="
[&_input]:border
[&_input]:border-gray-300
[&_input:focus]:border-blue-500
[&_input:focus]:ring-2
[&_input:focus]:ring-blue-200
">
<input type="text" placeholder="Focus me" />
<div>
<input type="email" placeholder="Or focus me" />
</div>
</div>

Common Patterns and Best Practices​

Me: What are some patterns I should know?

Frontend Mentor: Here are professional patterns:

Pattern 1: Form Container Styling​

<form className="
[&_input]:w-full
[&_input]:px-3
[&_input]:py-2
[&_input]:border
[&_input]:border-gray-300
[&_input]:rounded-md
[&_input:focus]:border-blue-500
[&_input:focus]:ring-2
[&_input:focus]:ring-blue-200
[&_label]:block
[&_label]:text-sm
[&_label]:font-medium
[&_label]:text-gray-700
[&_label]:mb-2
">
{/* All inputs and labels inside get consistent styling */}
</form>

Pattern 2: Button Group Styling​

<div className="
[&_button]:px-4
[&_button]:py-2
[&_button]:rounded-md
[&_button]:font-medium
[&_button]:transition-colors
[&_button:hover]:bg-gray-100
[&_button:focus]:outline-none
[&_button:focus]:ring-2
">
{/* All buttons get consistent styling */}
</div>

Pattern 3: Card Content Styling​

<div className="
[&_h3]:text-xl
[&_h3]:font-bold
[&_h3]:mb-2
[&_p]:text-gray-600
[&_p]:leading-relaxed
[&_a]:text-blue-600
[&_a:hover]:text-blue-800
[&_a:hover]:underline
">
{/* All headings, paragraphs, links get styled */}
</div>

When NOT to Use Arbitrary Variants​

Me: Should I always use these selectors?

Frontend Mentor: No! Here's when to use regular classes instead:

Use Regular Classes When:​

❌ Single element styling:

{/* Overkill */}
<div className="[&_button]:bg-blue-500">
<button>Only button</button>
</div>

{/* Better */}
<div>
<button className="bg-blue-500">Only button</button>
</div>

❌ Few elements with different styles:

{/* Overcomplicated */}
<div className="[&_button:nth-child(1)]:bg-blue-500 [&_button:nth-child(2)]:bg-red-500">
<button>Save</button>
<button>Delete</button>
</div>

{/* Better */}
<div>
<button className="bg-blue-500">Save</button>
<button className="bg-red-500">Delete</button>
</div>

Use Arbitrary Variants When:​

βœ… Many similar elements:

<form className="[&_input]:border [&_input]:px-3 [&_input]:py-2">
{/* 10+ input fields */}
</form>

βœ… Deeply nested structures:

<div className="[&_button]:rounded-lg">
{/* Buttons at various nesting levels */}
</div>

βœ… Consistent theming:

<article className="[&_a]:text-blue-600 [&_a:hover]:underline [&_code]:bg-gray-100">
{/* Blog post content with many links and code snippets */}
</article>

Debugging Tips​

Me: How do I debug if something doesn't work?

Frontend Mentor: Here are debugging strategies:

Tip 1: Check DevTools Computed Styles​

<div className="[&_button]:bg-blue-500">
<button>Test</button>
</div>
  1. Inspect the button
  2. Look for computed background-color
  3. If it's not blue, check:
    • Selector specificity
    • Typos in class name
    • Element type (is it actually a button?)

Tip 2: Test with Simple Styles First​

{/* Start simple */}
<div className="[&_button]:bg-red-500">
<button>Can you see red?</button>
</div>

{/* Then add complexity */}
<div className="[&_button]:bg-red-500 [&_button]:px-4">
<button>Now with padding</button>
</div>

Tip 3: Verify Nesting Level​

{/* If [&>button] doesn't work, try [&_button] */}
<div className="[&>button]:bg-blue-500"> ← Try this first
<button>Test</button>
</div>

{/* If still doesn't work */}
<div className="[&_button]:bg-blue-500"> ← Try this
<button>Test</button>
</div>

Key Takeaways​

Me: Let me summarize what I learned:

Frontend Mentor: Perfect! Here's the complete picture:

The Three Symbols:​

  1. & β†’ Self reference

    • [&]:hover β†’ Hover on current element
    • [&]:focus β†’ Focus on current element
  2. > β†’ Direct children (shallow)

    • [&>button] β†’ Only immediate button children
    • Stops at first nesting level
  3. _ β†’ All descendants (deep)

    • [&_button] β†’ ALL buttons at any level
    • Penetrates through all nesting

The Mental Model:​

Think of selectors as depth levels:

[&]:hover β†’ Self (0 levels)
[&>button] β†’ Children (1 level)
[&_button] β†’ Descendants (∞ levels)

Decision Tree:​

Need to style elements?
β”œβ”€ Is it the element itself?
β”‚ └─ Use [&]:pseudo-class
β”œβ”€ Only immediate children?
β”‚ └─ Use [&>element]
└─ All nested descendants?
└─ Use [&_element]

CSS Translation:​

TailwindCSS Equivalent
[&]:hover&:hover
[&>button]& > button
[&_button]& button

Remember:​

  • > = Shallow (direct children only)
  • _ = Deep (all descendants)
  • & = Self (the element itself)

Your Case:​

<div className="[&_button]:rounded-lg">
<button>βœ…</button>
<div className="flex gap-3">
<button>βœ…</button> ← Why you needed [&_button]!
</div>
</div>

The _ (underscore) penetrates through nested divs!


Me: This is amazing! I thought Tailwind was just about utility classes, but these arbitrary variants unlock so much power. Now I can style entire sections without repeating classes on every element!

Frontend Mentor: Exactly! Arbitrary variants are one of Tailwind's most powerful features. They let you write CSS selectors within Tailwind's utility-first system. You get the best of both worlds: utility classes for direct styling, and selectors for cascading styles. Now you're thinking like a Tailwind expert! πŸš€


Have you used arbitrary variants in your projects? Share your use cases in the comments! πŸ’¬