Documentation Index Fetch the complete documentation index at: https://mintlify.com/tailwindlabs/tailwindcss/llms.txt
Use this file to discover all available pages before exploring further.
Variants in Tailwind CSS allow you to apply styles conditionally based on state, viewport size, or other conditions. Tailwind v4 includes a comprehensive set of built-in variants and provides APIs for creating custom ones.
Built-in Variants
Tailwind includes many variants out of the box:
Pseudo-class Variants
Interactive States
Form States
Positional
< button class = "hover:bg-blue-600 focus:ring-2 active:scale-95" >
Interactive button
</ button >
Pseudo-element Variants
< p class = "first-line:uppercase first-letter:text-7xl" >
Styled text
</ p >
< div class = "before:content-['→'] after:content-['←']" >
Content with pseudo-elements
</ div >
< input class = "placeholder:italic placeholder:text-gray-400" placeholder = "Search..." />
Responsive
Dark Mode
Reduced Motion
< div class = "text-sm md:text-base lg:text-lg" >
Responsive text
</ div >
Variant Categories
State Variants
Applied on hover (with @media (hover: hover) wrapper) < button class = "hover:bg-blue-600" > Hover me </ button >
Applied when element has focus < input class = "focus:ring-2 focus:ring-blue-500" />
Applied when element is being activated < button class = "active:scale-95" > Press me </ button >
Applied to visited links < a class = "visited:text-purple-600" > Link </ a >
Applied when element is disabled < button class = "disabled:opacity-50" disabled > Disabled </ button >
<!-- Validation states -->
< input class = "valid:border-green-500 invalid:border-red-500" />
<!-- Checked states -->
< input type = "checkbox" class = "checked:bg-blue-600" />
<!-- Indeterminate state -->
< input type = "checkbox" class = "indeterminate:bg-gray-600" />
<!-- Required/Optional -->
< input class = "required:border-red-500 optional:border-gray-300" />
Positional Variants
< ul >
< li class = "first:pt-0" > First item </ li >
< li class = "last:pb-0" > Last item </ li >
< li class = "only:border-0" > Only item </ li >
< li class = "odd:bg-gray-100" > Odd items </ li >
< li class = "even:bg-white" > Even items </ li >
</ ul >
Group & Peer Variants
Apply styles based on parent or sibling state:
< div class = "group" >
< img class = "group-hover:opacity-75" />
< h3 class = "group-hover:text-blue-600" > Title </ h3 >
</ div >
Responsive Variants
The default breakpoints:
-- breakpoint - sm : 40 rem /* 640px */
-- breakpoint - md : 48 rem /* 768px */
-- breakpoint - lg : 64 rem /* 1024px */
-- breakpoint - xl : 80 rem /* 1280px */
-- breakpoint - 2 xl : 96 rem /* 1536px */
Usage:
< div class = "text-sm sm:text-base md:text-lg lg:text-xl" >
Responsive sizing
</ div >
Custom Variants with Plugins
Create custom variants using the plugin API:
Static Variant
Add a simple static variant:
import type { Config } from 'tailwindcss'
import plugin from 'tailwindcss/plugin'
export default {
plugins: [
plugin (({ addVariant }) => {
// Add a `third` variant
addVariant ( 'third' , '&:nth-child(3)' )
// Add a `hocus` variant (hover or focus)
addVariant ( 'hocus' , [ '&:hover' , '&:focus' ])
// Add an `optional` variant for optional form elements
addVariant ( 'optional' , '&:optional' )
// Add a `supports-grid` variant
addVariant ( 'supports-grid' , '@supports (display: grid)' )
}),
] ,
} satisfies Config
Usage:
< div class = "third:bg-blue-500" > Third child is blue </ div >
< button class = "hocus:ring-2" > Hover or focus for ring </ button >
Functional Variant
Create variants that accept values:
import plugin from 'tailwindcss/plugin'
export default {
plugins: [
plugin (({ matchVariant }) => {
// Add an `nth` variant
matchVariant (
'nth' ,
( value ) => `&:nth-child( ${ value } )` ,
)
// Add a `data` variant with values
matchVariant (
'data' ,
( value ) => `&[data- ${ value } ]` ,
{
values: {
checked: 'checked' ,
active: 'active' ,
disabled: 'disabled' ,
},
},
)
}),
] ,
}
Usage:
< div class = "nth-3:bg-blue-500" > Third child </ div >
< div class = "nth-[2n]:bg-gray-100" > Even children </ div >
< div class = "data-active:bg-green-500" data-active > Active state </ div >
Compound Variants
Create variants that modify other variants:
import plugin from 'tailwindcss/plugin'
export default {
plugins: [
plugin (({ addVariant }) => {
// Add a custom group variant
addVariant ( 'group-optional' , ':merge(.group):optional &' )
// Add a custom peer variant
addVariant ( 'peer-optional' , ':merge(.peer):optional ~ &' )
}),
] ,
}
Functional Variants
Some variants accept arbitrary values:
< div class = "nth-3:bg-blue-500" > 3rd child </ div >
< div class = "nth-[2n+1]:bg-gray-100" > Odd children </ div >
Stacking Variants
Combine multiple variants:
<!-- Responsive + hover -->
< button class = "md:hover:bg-blue-600" > Desktop hover </ button >
<!-- Dark mode + hover + focus -->
< button class = "dark:hover:focus:ring-4" > Complex state </ button >
<!-- Group + responsive + hover -->
< div class = "group-hover:md:scale-110" > Scales on group hover (desktop) </ div >
Variant Order
Variants are applied in a specific order (roughly):
Responsive variants (sm:, md:, etc.)
State variants (hover:, focus:, etc.)
Compound variants (group-*:, peer-*:)
Pseudo-elements (before:, after:, etc.)
The order of variants in your class name doesn’t matter - Tailwind handles ordering automatically based on CSS specificity rules.
Advanced Variant Examples
Custom Dark Mode
Implement a custom dark mode strategy:
export default {
darkMode: [ 'variant' , '&:is(.dark *)' ] ,
}
RTL Support
import plugin from 'tailwindcss/plugin'
export default {
plugins: [
plugin (({ addVariant }) => {
addVariant ( 'rtl' , '&:is([dir="rtl"] *)' )
addVariant ( 'ltr' , '&:is([dir="ltr"] *)' )
}),
] ,
}
Usage:
< div class = "ml-4 rtl:mr-4 rtl:ml-0" >
RTL-aware margin
</ div >
Container Queries
Use container query variants:
< div class = "@container" >
< div class = "@md:text-lg @lg:text-xl" >
Responsive to container size
</ div >
</ div >
Not Variant
Negate other variants:
< div class = "not-first:mt-4" > Margin except first </ div >
< div class = "not-last:border-b" > Border except last </ div >
< div class = "not-hover:opacity-75" > Opacity when not hovered </ div >
Variant Modifiers
Some variants support modifiers:
<!-- Named group -->
< div class = "group/card" >
< div class = "group-hover/card:opacity-100" >
Only affected by .group/card
</ div >
</ div >
<!-- Named peer -->
< input class = "peer/email" type = "email" />
< span class = "peer-invalid/email:visible" > Invalid email </ span >
Best Practices
Use semantic variants - hover:, focus:, etc. are more maintainable than arbitrary selectors
Leverage responsive variants - Build mobile-first with progressive enhancement
Group related states - Use group and peer for parent/sibling relationships
Be mindful of specificity - More variants = higher specificity
Stacking too many variants can create overly specific selectors that are hard to override. Consider extracting complex variant combinations into component classes.