Configuring Variants

Configuring which utility variants are enabled in your project.


Overview

The variants section of your tailwind.config.js file is where you control which core utility plugins should have responsive variants and pseudo-class variants generated.

// tailwind.config.js
module.exports = {
  variants: {
    appearance: ['responsive'],
    // ...
    borderColor: ['responsive', 'hover', 'focus'],
    // ...
    outline: ['responsive', 'focus'],
    // ...
    zIndex: ['responsive'],
  },
}

Each property is a core plugin name pointing to an array of variants to generate for that plugin. The following variants are supported out of the box:

  • 'responsive'
  • 'group-hover'
  • 'focus-within'
  • 'first'
  • 'last'
  • 'odd'
  • 'even'
  • 'hover'
  • 'focus'
  • 'active'
  • 'visited'
  • 'disabled'
  • 'motion-safe'
  • 'motion-reduce'

Overriding default variants

Any variants you specify will *override the default variants for that plugin.

// tailwind.config.js
module.exports = {
  variants: {
    // Only 'active' variants will be generated
    backgroundColor: ['active'],
  },
}

When overriding the default variants, make sure you always specify all the variants you'd like to enable, not just the new ones you'd like to add.

Extending default variants

If you'd like to enable extra variants for a plugin in addition to the defaults, you can configure your variants using a function instead of an array:

// tailwind.config.js
module.exports = {
  variants: {
    // The 'active' variant will be generated in addition to the defaults
    backgroundColor: ({ after }) => after(['active']),
  },
}

Because the order of variants is important, we provide a few helper functions as arguments that make it easy to add new variants in the right place.

before

The before helper lets you add new variants to the beginning of a plugin's default variant list.

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ before }) => before(['active']),
    // Output: ['active', 'responsive', 'hover', 'focus']
  },
}

If you'd like to add new variants before a specific variant in the list, pass that as the second argument:

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ before }) => before(['active'], 'focus'),
    // Output: ['responsive', 'hover', 'active', 'focus']
  },
}

after

The after helper lets you add new variants to the end of a plugin's default variant list.

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ after }) => after(['active']),
    // Output: ['responsive', 'hover', 'focus', 'active']
  },
}

If you'd like to add new variants after a specific variant in the list, pass that as the second argument:

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ after }) => after(['active'], 'hover'),
    // Output: ['responsive', 'hover', 'active', 'focus']
  },
}

without

The without helper lets you disable a variant that is enabled by default.

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ without }) => without(['focus']),
    // Output: ['responsive', 'hover']
  },
}

variants

The variants helper lets you retrieve the variants for a specific plugin to read from directly.

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ variants }) => [...variants('backgroundColor'), 'active'],
    // Output: ['responsive', 'hover', 'focus', 'active']
  },
}

This is an escape hatch and you probably won't ever need it. Stick to before, after, and without — they will handle everything you actually need.

Composing multiple helpers

Each helper can optionally take a list of variants as the final argument, and if provided, will apply against that list instead of the default variant list for the current plugin.

This makes it possible to compose them together and add variants both before and after, or add a variant before another variant while removing another variant, etc.

// tailwind.config.js
module.exports = {
  variants: {
    // Defaults are ['responsive', 'hover', 'focus']
    backgroundColor: ({ before, after, without }) => without(['focus'], before(['active'], 'hover', after(['focus-within'], 'responsive'))),
    // Output: [responsive', 'focus-within', 'active', 'hover']
  },
}

This looks complex and you probably won't need to do this often, but the power is there if you need it.


Ordering variants

It's important to note that variants are generated in the order you specify them, so variants at the end of the list will take precedence over variants at the beginning of the list.

In this example, focus variants have the highest precedence for backgroundColor utilities, but hover variants have the highest precedence for borderColor utilities:

// tailwind.config.js
module.exports = {
  variants: {
    backgroundColor: ['hover', 'focus'],
    borderColor: ['focus', 'hover'],
  },
}
/* Generated CSS */

.bg-black { background-color: #000 }
.bg-white { background-color: #fff }
/* ... */

.hover\:bg-black:hover { background-color: #000 }
.hover\:bg-white:hover { background-color: #fff }
/* ... */

.focus\:bg-black:focus { background-color: #000 }
.focus\:bg-white:focus { background-color: #fff }
/* ... */

.border-black { border-color: #000 }
.border-white { border-color: #fff }
/* ... */

.focus\:border-black:focus { border-color: #000 }
.focus\:border-white:focus { border-color: #fff }
/* ... */

.hover\:border-black:hover { border-color: #000 }
.hover\:border-white:hover { border-color: #fff }
/* ... */

This means that given the following HTML:

<input class="focus:bg-white hover:bg-black focus:border-white hover:border-black">

...if the input was hovered and focused at the same time, the background would be white but the border would be black.

Generally, we recommend the following order for the built-in variants, although you are free to use whatever order makes the most sense for your own project:

['responsive', 'group-hover', 'group-focus', 'focus-within', 'first', 'last', 'odd', 'even', 'hover', 'focus', 'active', 'visited', 'disabled']

The responsive variant

The responsive variant is the only variant that is not impacted by the order you list in your variants configuration.

This is because the responsive variant automatically stacks with pseudo-class variants, meaning that if you specify both responsive and hover variants for a utility, Tailwind will generate responsive hover variants as well:

// tailwind.config.js
module.exports = {
  variants: {
    backgroundColor: ['responsive', 'hover'],
    borderColor: ['responsive', 'focus'],
  },
}
/* Generated CSS */

.bg-black { background-color: #000 }
/* ... */
.hover\:bg-black:hover { background-color: #000 }
/* ... */

.border-black { border-color: #000 }
/* ... */
.focus\:border-black:focus { border-color: #000 }
/* ... */


@media (min-width: 640px) {
  .sm\:bg-black { background-color: #000 }
  /* ... */
  .sm\:hover\:bg-black:hover { background-color: #000 }
  /* ... */

  .sm\:border-black { border-color: #000 }
  /* ... */
  .sm\:focus\:border-black:focus { border-color: #000 }
  /* ... */
}

@media (min-width: 768px) {
  .md\:bg-black { background-color: #000 }
  /* ... */
  .md\:hover\:bg-black:hover { background-color: #000 }
  /* ... */

  .md\:border-black { border-color: #000 }
  /* ... */
  .md\:focus\:border-black:focus { border-color: #000 }
  /* ... */
}

@media (min-width: 1024px) {
  .lg\:bg-black { background-color: #000 }
  /* ... */
  .lg\:hover\:bg-black:hover { background-color: #000 }
  /* ... */

  .lg\:border-black { border-color: #000 }
  /* ... */
  .lg\:focus\:border-black:focus { border-color: #000 }
  /* ... */
}

@media (min-width: 1280px) {
  .xl\:bg-black { background-color: #000 }
  /* ... */
  .xl\:hover\:bg-black:hover { background-color: #000 }
  /* ... */

  .xl\:border-black { border-color: #000 }
  /* ... */
  .xl\:focus\:border-black:focus { border-color: #000 }
  /* ... */
}

Responsive variants are grouped together and inserted at the end of your stylesheet by default to avoid specificity issues. If you'd like to customize this behavior for whatever reason, you can use the @tailwind screens directive to specify where responsive variants should be inserted.

The default variant

You can use the special default variant to control where the normal, non-prefixed versions of a utility are generated relative to the other variants.

// tailwind.config.js
module.exports = {
  variants: {
    backgroundColor: ['hover', 'default', 'focus'],
  },
}
/* Generated CSS */

.hover\:bg-black:hover { background-color: #000 }
.hover\:bg-white:hover { background-color: #fff }
/* ... */

.bg-black { background-color: #000 }
.bg-white { background-color: #fff }
/* ... */

.focus\:bg-black:focus { background-color: #000 }
.focus\:bg-white:focus { background-color: #fff }
/* ... */

This is an advanced feature and only really useful if you have a custom variant (like children in the example below) that should have a lower precedence than the normal version of a utility.

// tailwind.config.js
module.exports = {
  variants: {
    backgroundColor: ['children', 'default', 'hover', 'focus'],
  },
}
/* Generated CSS */

.children\:bg-black > * { background-color: #000; }
.children\:bg-white > * { background-color: #fff; }

.bg-black { background-color: #000 }
.bg-white { background-color: #fff }
/* ... */

.hover\:bg-black:hover { background-color: #000 }
.hover\:bg-white:hover { background-color: #fff }
/* ... */

.focus\:bg-black:focus { background-color: #000 }
.focus\:bg-white:focus { background-color: #fff }
/* ... */

Learn more about creating custom variants in the variant plugin documentation.


Enabling variants globally

To specify a global set of variants that should be applied to all utilities, you can assign an array of variants directly to the variants property instead of configuring variants separately:

// tailwind.config.js
module.exports  = {
  variants: ['responsive', 'group-hover', 'disabled', 'hover', 'focus', 'active']
}

Note that enabling a lot of variants for all plugins will result in much bigger file sizes. Before you do this, be sure to read our guide on Controlling File Size.


Using custom variants

If you've written or installed a plugin that adds a new variant, you can enable that variant by including it in your variants configuration just like if it were a built-in variant.

For example, the tailwindcss-interaction-variants plugin adds a group-disabled variant (among others):

// tailwind.config.js
{
  variants: {
    backgroundColor: ['responsive', 'hover', 'focus', 'group-disabled'],
  },
  plugins: [
    require('tailwindcss-interaction-variants')(),
  ],
}

Learn more about creating custom variants in the variant plugin documentation.


Default variants reference

Here is a complete reference of Tailwind's default variants configuration, which can be useful when you'd like to add a new variant while preserving the defaults.

// 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!