Wait, What's [&_button] in Tailwind? Understanding &, >, and _ Selectors for Nested Elements
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β
&β "The current element itself" (parent reference)>β "Only my direct children"_β "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:
| Selector | Tailwind Syntax | CSS Equivalent | Targets | Example |
|---|---|---|---|---|
| Self | [&]:hover | &:hover | The element itself | Hover states, focus states |
| Direct children | [&>button] | & > button | Only immediate children | Top-level buttons only |
| All descendants | [&_button] | & button | All nested children | Buttons 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>
- Inspect the button
- Look for computed background-color
- 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:β
-
&β Self reference[&]:hoverβ Hover on current element[&]:focusβ Focus on current element
-
>β Direct children (shallow)[&>button]β Only immediate button children- Stops at first nesting level
-
_β 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:β
| Tailwind | CSS 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! π¬
