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.

One
Two
Three
<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.

One
Two
Three
<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.

One
Two
Three
<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.

One
Two
Three
<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']
  }
}

Tailwind UI is now in early access!