Pseudo-Class Variants
Overview
Similar to how Tailwind handles responsive design, styling elements on hover, focus, and more can be accomplished by prefixing utilities with the appropriate pseudo-class.
<form>
<input class="bg-gray-200 hover:bg-white hover:border-gray-300 focus:outline-none focus:bg-white focus:shadow-outline focus:border-gray-300 ...">
<button class="bg-teal-500 hover:bg-teal-600 focus:outline-none focus:shadow-outline ...">
Sign Up
</button>
</form>
Not all pseudo-class variants are enabled for all utilities by default due to file-size considerations, but we've tried our best to enable the most commonly used combinations out of the box.
For a complete list of which variants are enabled by default, see the reference table at the end of this page.
If you need to target a pseudo-class that Tailwind doesn't support, you can extend the supported variants by writing a variant plugin.
Hover
Add the hover:
prefix to only apply a utility on hover.
<button class="bg-transparent hover:bg-blue-500 text-blue-700 hover:text-white...">
Hover me
</button>
You can control whether hover
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
Focus
Add the focus:
prefix to only apply a utility on focus.
<input class="bg-gray-200 focus:bg-white border-transparent focus:border-blue-400 ..." placeholder="Focus me">
You can control whether focus
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus'],
},
}
Active
Add the active:
prefix to only apply a utility when an element is active.
<button class="bg-blue-500 active:bg-blue-700 text-white...">
Click me
</button>
You can control whether active
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'active'],
},
}
Group-hover
If you need to style a child element when hovering over a specific parent element, add the .group
class to the parent element and add the group-hover:
prefix to the utility on the child element.
New Project
Create a new project from a variety of starting templates.
<div class="group bg-white hover:bg-blue-500 ...">
<p class="text-gray-900 group-hover:text-white ...">New Project</p>
<p class="text-gray-700 group-hover:text-white ...">Create a new project from a variety of starting templates.</p>
</div>
You can control whether group-hover
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
textColor: ['responsive', 'hover', 'focus', 'group-hover'],
},
}
Group-focusv1.3.0+
The group-focus
variant works just like group-hover
except for focus:
<button class="group text-gray-700 focus:text-gray-900 ...">
<svg class="h-6 w-6 text-gray-400 group-focus:text-gray-500"><!-- ... --></svg>
Submit
</button>
You can control whether group-focus
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
textColor: ['responsive', 'hover', 'focus', 'group-focus'],
},
}
Focus-within
Note that focus-within is not supported in IE or Edge < 79.
Add the focus-within:
prefix to only apply a utility when a child element has focus.
<form class="border-b-2 border-gray-400 focus-within:border-teal-500 ...">
<input class="..." placeholder="Jane Doe" ...>
<button class="...">
Sign Up
</button>
</form>
You can control whether focus-within
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
borderColor: ['responsive', 'hover', 'focus', 'focus-within'],
},
}
Focus-visiblev1.5.0+
Note that focus-visible currently requires a polyfill for sufficient browser support.
Add the focus-visible:
prefix to only apply a utility when an element has focus but only if the user is using the keyboard.
<ul class="flex space-x-8">
<li>
<a class="focus:outline-none focus:underline ..." href="#">
Underlined on focus
</a>
</li>
<li>
<a class="focus:outline-none focus-visible:underline ..." href="#">
Underlined on focus-visible
</a>
</li>
</ul>
Note that only Firefox supports focus-visible
natively, so for sufficient browser support you should install and configure both the focus-visible JS polyfill and the focus-visible PostCSS polyfill. Make sure to include the PostCSS plugin after Tailwind in your list of PostCSS plugins:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
'postcss-focus-visible': {},
autoprefixer: {}
}
}
You can control whether focus-visible
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
textDecoration: ['responsive', 'hover', 'focus', 'focus-visible'],
},
}
Motion-safev1.6.0+
Add the motion-safe:
prefix to only apply a utility when the prefers-reduced-motion
media feature matches no-preference
.
For example, this button will only animate on hover if the user hasn't enabled "Reduce motion" on their system.
<button class="transform motion-safe:hover:-translate-y-1 motion-safe:hover:scale-110 transition ease-in-out duration-300 ...">
Hover
</button>
Note that unlike most other variants, motion-safe
can be combined with both responsive variants and other variants like hover
, by stacking them in this order:
<div class="sm:motion-safe:hover:animate-spin">
<!-- ... -->
</div>
You can control whether motion-safe
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
translate: ['responsive', 'hover', 'focus', 'motion-safe'],
},
}
Motion-reducev1.6.0+
Add the motion-reduce:
prefix to only apply a utility when the prefers-reduced-motion
media feature matches reduce
.
For example, this button will animate on hover by default, but the animations will be disabled if the user has enabled "Reduce motion" on their system.
<button class="transform motion-reduce:transform-none hover:-translate-y-1 hover:scale-110 transition ease-in-out duration-300 ...">
Hover
</button>
Note that unlike most other variants, motion-reduce
can be combined with both responsive variants and other variants like hover
, by stacking them in this order:
<div class="sm:motion-reduce:hover:animate-none">
<!-- ... -->
</div>
You can control whether motion-reduce
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
translate: ['responsive', 'hover', 'focus', 'motion-reduce'],
},
}
Disabledv1.1.0+
Add the disabled:
prefix to only apply a utility when an element is disabled.
<button class="disabled:opacity-75 bg-blue-500...">
Submit
</button>
<button disabled class="disabled:opacity-75 bg-blue-500...">
Submit
</button>
You can control whether disabled
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
opacity: ['responsive', 'hover', 'focus', 'disabled'],
},
}
Visitedv1.1.0+
Add the visited:
prefix to only apply a utility when a link has been visited.
<a href="#" class="text-blue-600 visited:text-purple-600 ...">Link</a>
You can control whether visited
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
textColor: ['responsive', 'hover', 'focus', 'visited'],
},
}
Checkedv1.5.0+
Add the checked:
prefix to only apply a utility when a radio or checkbox is currently checked.
<input type="radio" class="appearance-none checked:bg-gray-900 checked:border-transparent ...">
You can control whether checked
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'hover', 'focus', 'checked'],
},
}
First-childv1.1.0+
Add the first:
prefix to only apply a utility when it is the first-child of its parent. This is mostly useful when elements are being generated in some kind of loop.
<div class="border rounded">
<div v-for="item in items" class="border-t first:border-t-0">
{{ item }}
</div>
</div>
It's important to note that you should add any first:
utilities to the child element, not the parent element.
You can control whether first
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
borderWidth: ['responsive', 'first', 'hover', 'focus'],
},
}
Last-childv1.1.0+
Add the last:
prefix to only apply a utility when it is the last-child of its parent. This is mostly useful when elements are being generated in some kind of loop.
<div class="border rounded">
<div v-for="item in items" class="border-b last:border-b-0">
{{ item }}
</div>
</div>
It's important to note that you should add any last:
utilities to the child element, not the parent element.
You can control whether last
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
borderWidth: ['responsive', 'last', 'hover', 'focus'],
},
}
Odd-childv1.1.0+
Add the odd:
prefix to only apply a utility when it is an odd-child of its parent. This is mostly useful when elements are being generated in some kind of loop.
<div class="border rounded">
<div v-for="item in items" class="bg-white odd:bg-gray-200">
{{ item }}
</div>
</div>
It's important to note that you should add any odd:
utilities to the child element, not the parent element.
You can control whether odd
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'odd', 'hover', 'focus'],
},
}
Even-childv1.1.0+
Add the even:
prefix to only apply a utility when it is an even-child of its parent. This is mostly useful when elements are being generated in some kind of loop.
<div class="border rounded">
<div v-for="item in items" class="bg-white even:bg-gray-200">
{{ item }}
</div>
</div>
It's important to note that you should add any even:
utilities to the child element, not the parent element.
You can control whether even
variants are enabled for a utility in the variants
section of your tailwind.config.js
file:
// tailwind.config.js
module.exports = {
// ...
variants: {
backgroundColor: ['responsive', 'even', 'hover', 'focus'],
},
}
Combining with responsive prefixes
Pseudo-class variants are also responsive, meaning you can do things like change an element's hover style at different breakpoints for example.
To apply a pseudo-class variant at a specific breakpoint, add the responsive prefix first, before the pseudo-class prefix:
<div class="bg-orange-500 hover:bg-orange-600 sm:bg-green-500 sm:hover:bg-green-600 md:bg-red-500 md:hover:bg-red-600 lg:bg-indigo-500 lg:hover:bg-indigo-600 xl:bg-pink-500 xl:hover:bg-pink-600"></div>
Generative variants for custom utilities
You can generate pseudo-class variants for your own custom utilities by wrapping them with the @variants
directive in your CSS:
/* Input: */
@variants group-hover, hover, focus {
.banana {
color: yellow;
}
}
/* Output: */
.banana {
color: yellow;
}
.group:hover .group-hover\:banana {
color: yellow;
}
.hover\:banana:hover {
color: yellow;
}
.focus\:banana:focus {
color: yellow;
}
For more information, see the @variants directive documentation.
Creating custom variants
You can create your own variants for any pseudo-classes Tailwind doesn't include by default by writing a custom variant plugin.
For example, this simple plugin adds support for the disabled
pseudo-class variant:
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function({ addVariant, e }) {
addVariant('disabled', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
return `.${e(`disabled${separator}${className}`)}:disabled`
})
})
})
]
}
Learn more about writing variant plugins in the variant plugin documentation.
Default variants reference
Due to file-size considerations, Tailwind does not include all variants for all utilities by default.
To configure which variants are enabled for your project, see the configuring variants documentation.
// Default configuration
module.exports = {
// ...
variants: {
accessibility: ['responsive', 'focus']
alignContent: ['responsive']
alignItems: ['responsive']
alignSelf: ['responsive']
appearance: ['responsive']
backgroundAttachment: ['responsive']
backgroundColor: ['responsive', 'hover', 'focus']
backgroundOpacity: ['responsive', 'hover', 'focus']
backgroundPosition: ['responsive']
backgroundRepeat: ['responsive']
backgroundSize: ['responsive']
borderCollapse: ['responsive']
borderColor: ['responsive', 'hover', 'focus']
borderOpacity: ['responsive', 'hover', 'focus']
borderRadius: ['responsive']
borderStyle: ['responsive']
borderWidth: ['responsive']
boxShadow: ['responsive', 'hover', 'focus']
boxSizing: ['responsive']
container: ['responsive']
cursor: ['responsive']
display: ['responsive']
divideColor: ['responsive']
divideOpacity: ['responsive']
divideWidth: ['responsive']
fill: ['responsive']
flex: ['responsive']
flexDirection: ['responsive']
flexGrow: ['responsive']
flexShrink: ['responsive']
flexWrap: ['responsive']
float: ['responsive']
clear: ['responsive']
fontFamily: ['responsive']
fontSize: ['responsive']
fontSmoothing: ['responsive']
fontStyle: ['responsive']
fontWeight: ['responsive', 'hover', 'focus']
height: ['responsive']
inset: ['responsive']
justifyContent: ['responsive']
letterSpacing: ['responsive']
lineHeight: ['responsive']
listStylePosition: ['responsive']
listStyleType: ['responsive']
margin: ['responsive']
maxHeight: ['responsive']
maxWidth: ['responsive']
minHeight: ['responsive']
minWidth: ['responsive']
objectFit: ['responsive']
objectPosition: ['responsive']
opacity: ['responsive', 'hover', 'focus']
order: ['responsive']
outline: ['responsive', 'focus']
overflow: ['responsive']
overscrollBehavior: ['responsive']
padding: ['responsive']
placeholderColor: ['responsive', 'focus']
placeholderOpacity: ['responsive', 'focus']
pointerEvents: ['responsive']
position: ['responsive']
resize: ['responsive']
space: ['responsive']
stroke: ['responsive']
strokeWidth: ['responsive']
tableLayout: ['responsive']
textAlign: ['responsive']
textColor: ['responsive', 'hover', 'focus']
textOpacity: ['responsive', 'hover', 'focus']
textDecoration: ['responsive', 'hover', 'focus']
textTransform: ['responsive']
userSelect: ['responsive']
verticalAlign: ['responsive']
visibility: ['responsive']
whitespace: ['responsive']
width: ['responsive']
wordBreak: ['responsive']
zIndex: ['responsive']
gap: ['responsive']
gridAutoFlow: ['responsive']
gridTemplateColumns: ['responsive']
gridColumn: ['responsive']
gridColumnStart: ['responsive']
gridColumnEnd: ['responsive']
gridTemplateRows: ['responsive']
gridRow: ['responsive']
gridRowStart: ['responsive']
gridRowEnd: ['responsive']
transform: ['responsive']
transformOrigin: ['responsive']
scale: ['responsive', 'hover', 'focus']
rotate: ['responsive', 'hover', 'focus']
translate: ['responsive', 'hover', 'focus']
skew: ['responsive', 'hover', 'focus']
transitionProperty: ['responsive']
transitionTimingFunction: ['responsive']
transitionDuration: ['responsive']
transitionDelay: ['responsive']
animation: ['responsive']
}
}