Pseudo-Class Variants
Using utilities to style elements on hover, focus, and more.
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 currently only Firefox and Chrome support 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'],
backgroundClip: ['responsive'],
backgroundColor: ['responsive', 'hover', 'focus'],
backgroundImage: ['responsive'],
gradientColorStops: ['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'],
divideStyle: ['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'],
fontVariantNumeric: ['responsive'],
fontStyle: ['responsive'],
fontWeight: ['responsive', 'hover', 'focus'],
height: ['responsive'],
inset: ['responsive'],
justifyContent: ['responsive'],
justifyItems: ['responsive'],
justifySelf: ['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'],
placeContent: ['responsive'],
placeItems: ['responsive'],
placeSelf: ['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'],
gridAutoColumns: ['responsive'],
gridColumn: ['responsive'],
gridColumnStart: ['responsive'],
gridColumnEnd: ['responsive'],
gridTemplateRows: ['responsive'],
gridAutoRows: ['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']
}
}