Tailwind CSS on GitHub

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 variants should be enabled for each core plugin:

// tailwind.config.js
module.exports = {
  variants: {
    extend: {
      backgroundColor: ['active'],
      // ...
      borderColor: ['focus-visible', 'first'],
      // ...
      textColor: ['visited'],
    }
  },
}

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:

VariantDescription
responsiveResponsive variants like sm, md, lg, and xl.
darkTargets dark mode.
motion-safeTargets the prefers-reduced-motion: no-preference media query.
motion-reduceTargets the prefers-reduced-motion: reduce media query.
firstTargets the first-child pseudo-class.
lastTargets the last-child pseudo-class.
oddTargets the odd-child pseudo-class.
evenTargets the even-child pseudo-class.
visitedTargets the visited pseudo-class.
checkedTargets the checked pseudo-class.
group-hoverTargets an element when a marked parent matches the hover pseudo-class.
group-focusTargets an element when a marked parent matches the focus pseudo-class.
focus-withinTargets the focus-within pseudo-class.
hoverTargets the hover pseudo-class.
focusTargets the focus pseudo-class.
focus-visibleTargets the focus-visible pseudo-class.
activeTargets the active pseudo-class.
disabledTargets the disabled pseudo-class.

For more information about how variants work, read our documentation on responsive variants, dark mode variants, and hover, focus and other state variants.


Enabling extra variants

If you'd like to enable extra variants for a plugin in addition to the defaults, you can configure your variants using the extend keyword, similar to how you can use extend inside of the theme section:

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

Because the order of variants is important, any variants added under the extend key are automatically ordered for you using a sensible default variant order. You can customize this order using the variantOrder option if necessary.


Overriding default variants

Any variants configured directly under the variants key 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.

Ordering variants

It's important to note that when overriding variants, 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.

For example, here 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.

Generating variants in order this way gives you the most flexibility as an end-user, but it's also a sharp tool and can have unintended consequences if you aren't careful. We recommend enabling extra variants instead of overriding the defaults whenever possible, and using this feature only as an escape hatch.


Special variants

Responsive

The responsive variant is a special case in Tailwind and is not impacted by the order you list in your variants configuration.

This is because the responsive variant automatically stacks with other 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'],
  },
}

Responsive variants are grouped together and inserted at the end of your stylesheet by default to avoid specificity issues, regardless of where responsive appears in your variants list.

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.

Dark, motion-safe, and motion-reduce

The dark, motion-safe, and motion-reduce variants also stack with other variants, but unlike responsive, they stack in the same "slot", so you can combine them with both responsive and simple state variants, but not with each other.

The order of these variants matter relative to each other, but not relative to other variants. There is just about no situation imaginable where these would conflict with each other in practice, so this ends up being a non-issue anyways.

You can include these variants in any order in your variants configuration and never notice the difference.

Default

You can use the special DEFAULT variant to control where the normal, non-prefixed version of a utility is generated relative to the other variants.

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.


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.

Ordering custom variants

If you'd like to specify a default sort position for a custom variant, override your variantOrder to include the custom variant:

// tailwind.config.js
module.exports = {
  // ...
  variantOrder: [
    'first',
    'last',
    'odd',
    'even',
    'visited',
    'checked',
    'group-hover',
    'group-focus',
    'focus-within',
    'hover',
    'focus',
    'focus-visible',
    'active',
    'group-disabled', // Custom variant
    'disabled',
  ],
  variants: {
    extend: {
      backgroundColor: ['group-disabled'],
    }
  }
}

You'll need to specify the entire list when overriding the variantOrder to include any custom variants.


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-within', 'focus'],
    alignContent: ['responsive'],
    alignItems: ['responsive'],
    alignSelf: ['responsive'],
    animation: ['responsive'],
    appearance: ['responsive'],
    backgroundAttachment: ['responsive'],
    backgroundClip: ['responsive'],
    backgroundColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    backgroundImage: ['responsive'],
    backgroundOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    backgroundPosition: ['responsive'],
    backgroundRepeat: ['responsive'],
    backgroundSize: ['responsive'],
    borderCollapse: ['responsive'],
    borderColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    borderOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    borderRadius: ['responsive'],
    borderStyle: ['responsive'],
    borderWidth: ['responsive'],
    boxShadow: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
    boxSizing: ['responsive'],
    clear: ['responsive'],
    container: ['responsive'],
    cursor: ['responsive'],
    display: ['responsive'],
    divideColor: ['responsive', 'dark'],
    divideOpacity: ['responsive', 'dark'],
    divideStyle: ['responsive'],
    divideWidth: ['responsive'],
    fill: ['responsive'],
    flex: ['responsive'],
    flexDirection: ['responsive'],
    flexGrow: ['responsive'],
    flexShrink: ['responsive'],
    flexWrap: ['responsive'],
    float: ['responsive'],
    fontFamily: ['responsive'],
    fontSize: ['responsive'],
    fontSmoothing: ['responsive'],
    fontStyle: ['responsive'],
    fontVariantNumeric: ['responsive'],
    fontWeight: ['responsive'],
    gap: ['responsive'],
    gradientColorStops: ['responsive', 'dark', 'hover', 'focus'],
    gridAutoColumns: ['responsive'],
    gridAutoFlow: ['responsive'],
    gridAutoRows: ['responsive'],
    gridColumn: ['responsive'],
    gridColumnEnd: ['responsive'],
    gridColumnStart: ['responsive'],
    gridRow: ['responsive'],
    gridRowEnd: ['responsive'],
    gridRowStart: ['responsive'],
    gridTemplateColumns: ['responsive'],
    gridTemplateRows: ['responsive'],
    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', 'group-hover', 'focus-within', 'hover', 'focus'],
    order: ['responsive'],
    outline: ['responsive', 'focus-within', 'focus'],
    overflow: ['responsive'],
    overscrollBehavior: ['responsive'],
    padding: ['responsive'],
    placeContent: ['responsive'],
    placeItems: ['responsive'],
    placeSelf: ['responsive'],
    placeholderColor: ['responsive', 'dark', 'focus'],
    placeholderOpacity: ['responsive', 'dark', 'focus'],
    pointerEvents: ['responsive'],
    position: ['responsive'],
    resize: ['responsive'],
    ringColor: ['responsive', 'dark', 'focus-within', 'focus'],
    ringOffsetColor: ['responsive', 'dark', 'focus-within', 'focus'],
    ringOffsetWidth: ['responsive', 'focus-within', 'focus'],
    ringOpacity: ['responsive', 'dark', 'focus-within', 'focus'],
    ringWidth: ['responsive', 'focus-within', 'focus'],
    rotate: ['responsive', 'hover', 'focus'],
    scale: ['responsive', 'hover', 'focus'],
    skew: ['responsive', 'hover', 'focus'],
    space: ['responsive'],
    stroke: ['responsive'],
    strokeWidth: ['responsive'],
    tableLayout: ['responsive'],
    textAlign: ['responsive'],
    textColor: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    textDecoration: ['responsive', 'group-hover', 'focus-within', 'hover', 'focus'],
    textOpacity: ['responsive', 'dark', 'group-hover', 'focus-within', 'hover', 'focus'],
    textOverflow: ['responsive'],
    textTransform: ['responsive'],
    transform: ['responsive'],
    transformOrigin: ['responsive'],
    transitionDelay: ['responsive'],
    transitionDuration: ['responsive'],
    transitionProperty: ['responsive'],
    transitionTimingFunction: ['responsive'],
    translate: ['responsive', 'hover', 'focus'],
    userSelect: ['responsive'],
    verticalAlign: ['responsive'],
    visibility: ['responsive'],
    whitespace: ['responsive'],
    width: ['responsive'],
    wordBreak: ['responsive'],
    zIndex: ['responsive', 'focus-within', 'focus']
  }
}