Introduction
Tailwind CSS arbitrary variants allow you to create custom styles that target specific elements in your HTML structure. This is particularly useful when you need to apply styles to child elements based on their type, class, or attributes without adding additional CSS classes or JavaScript.
While the official documentation covers the basics, it doesn't demonstrate how to handle more complex scenarios like targeting deeply nested children, combining multiple selectors, or working with custom attributes.
Use arbitrary variants sparingly as they can reduce code readability and maintainability.
Core Concepts
Understanding the & Symbol
The & character represents the current element (the one with the arbitrary variant class). Think of it as "this element" in your selector.
Direct Children vs All Descendants
This is the most important distinction to understand:
>- Targets only direct children (immediate descendants)_- Targets all descendants at any nesting level
<div className="[&>p]:text-red-500 [&_span]:text-blue-500">
<p>Direct child - will be red</p>
<div>
<p>Nested child - won't be red (not direct)</p>
<span>Nested span - will be blue (all descendants)</span>
</div>
<span>Direct span - will be blue</span>
</div>Direct child - will be red
Nested child - won't be red (not direct)
Nested span - will be blue (all descendants)Targeting Children by Tag
When you need to target direct children by their tag, you can use the & selector followed by the tag name.
<div className="[&>p]:text-red-600">
<span>Black text</span>
<p>Red text</p>
</div>Red text
Targeting Children by Class
When you need to target direct children by their class, you can use the & selector with the class name prefixed by a dot.
This also works with className attributes in React components.
<div className="[&>.child2]:text-red-600">
<div className="child1">Black text</div>
<div className="child2">Red text</div>
</div>You can also add additional classes to the selector.
<div className="[&>div.child.foo]:text-red-600">
<div className="child">Black text</div>
<div className="child foo">Red text</div>
</div>Targeting Children by Data Attributes
When you need to target elements based on their data attributes, you can use the & selector with the attribute wrapped in square brackets.
This also works with other attributes, not just data-*, for example, role, aria-*, or custom attributes.
Target immediate children with specific data attributes.
<div className="[&>[data-type='special']]:text-red-600">
<div data-type="normal">Black text</div>
<div data-type="special">Red text</div>
</div>You can also target nested elements with data attributes.
<div className="[&_[data-type='special']]:text-red-600">
<div>
<div data-type="special">Red text</div>
</div>
</div>When you need to target elements based on both their attributes and classes, you can combine them in the selector.
<div className="[&_[data-type='a'].red]:text-red-500 [&_[data-type='a'].blue]:text-blue-500">
<p data-type="a" className="red">Red text</p>
<p data-type="a" className="blue">Blue text</p>
</div>Red text
Blue text
Targeting Nested Child Elements
When you need to target deeply nested child elements, you can use the & selector with a path that describes the hierarchy.
<div className="[&>div>div>span]:text-red-600">
<div>
<div>
<span>Deep Child</span>
</div>
</div>
</div>Conditional Styling Based on Parent Tag
When you need to style child elements based on the tag of their parent, you can use the & selector in combination with the parent tag.
<p>
<span className="[p_&]:text-red-600">Styled if parent is p</span>
</p>
<div>
<span className="[p_&]:text-red-600">Styled if parent is p</span>
</div>Styled if parent is p
Conditional Styling Based on Parent Class
When you need to style child elements based on the class of their parent, you can use the & selector in combination with the parent class.
<div className="foo">
<div>
<div className="[.foo_&]:text-red-600">Child 1</div>
<div className="[.bar_&]:text-red-600">Child 2</div>
</div>
</div>Conditional Styling Based on Parent Attribute
When you need to style child elements based on a specific attribute of their parent, you can use the & selector with the attribute wrapped in square brackets.
<div data-type="special">
<p className="[[data-type='special']_&]:text-red-600">Styled if parent has data-type="special"</p>
</div>
<div>
<p className="[[data-type='special']_&]:text-red-600">Not styled</p>
</div>Styled if parent has data-type="special"
Not styled
Targeting by nth-child
You can use the & selector with :nth-child(n) to target specific children based on their position within the parent.
<div className="[&>*:nth-child(3)]:text-red-600">
<p>One</p>
<p>Two</p>
<p>Three</p>
</div>One
Two
Three
To combine multiple nth-child selectors to target different children, you need to add another & selector for each child you want to style.
<div className="[&>*:nth-child(1)]:text-red-600 [&>*:nth-child(3)]:text-red-600">
<p>One</p>
<p>Two</p>
<p>Three</p>
</div>One
Two
Three
Advanced Combinations
Target second child inside .foo only
When you want to target a specific child element inside a parent with a specific class, you can use the & selector with :nth-child(n).
<div className="[&>div.foo>*:nth-child(2)]:text-red-600">
<div className="foo">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
<div className="bar">
<p>Not affected</p>
</div>
</div>1
2
3
Conditional nth-child with Exclusion
When you need to exclude certain elements from a universal styling rule, you can use the :not() pseudo-class.
<div className="*:[&:not(.excluded)]:text-red-600">
<div>
<div>Not Excluded</div>
</div>
<div className="excluded">
<div>Excluded</div>
</div>
</div><div className="*:[&:not([excluded])]:text-red-600">
<div>
<div>Not Excluded</div>
</div>
<div excluded>
<div>Excluded</div>
</div>
</div>Pseudo + Tag + Descendant + Modifier
When you need to style elements based on a combination of pseudo-classes, tags, and modifiers, you can use the & selector with a complex path.
<div className="[div:has(span)&>p]:text-red-600">
<span>Trigger</span>
<p>Styled paragraph</p> <!-- Styled because parent has span -->
</div>
<div className="[div:has(span)&>p]:text-red-600">
<div>Trigger</div>
<p>Styled paragraph</p>
</div>Styled paragraph
Styled paragraph
Conclusion
Tailwind CSS arbitrary variants provide powerful tools for targeting child elements, styling based on parent context, and handling complex structures without the need for custom CSS or JavaScript. However, they should be used with care to maintain readability and code maintainability.
Cheat Sheet
| Selector | Use Case |
|---|---|
[&>tag] | Target direct child elements by tag |
[&_tag] | Target all descendant elements by tag |
[&>.class] | Target direct children by class |
[&_.class] | Target all descendants by class |
[&>div.class1.class2] | Target direct children with multiple classes |
[&>[data-x="y"]] | Target direct children with data attributes |
[&_[data-x="y"]] | Target any nested element with data attributes |
[&_[data-type='a'].class] | Combine attributes and classes |
[&>div>div>span] | Target deeply nested children with specific path |
[tag_&] | Style based on parent's tag |
[.parent_&] | Style based on parent's class |
[[data-x="y"]_&] | Style based on parent's attributes |
[&>*:nth-child(n)] | Target children by position |
[&>div.class>*:nth-child(n)] | Target nth-child within specific parent |
*:[&:not(.excluded)] | Exclude elements from universal styling |
*:[&:not([excluded])] | Exclude elements by attribute |
[div:has(span)&>p] | Style based on parent containing specific child |


