Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tailwindlabs/tailwindcss/llms.txt

Use this file to discover all available pages before exploring further.

Variants in Tailwind CSS allow you to apply styles conditionally based on state, viewport size, or other conditions. Tailwind v4 includes a comprehensive set of built-in variants and provides APIs for creating custom ones.

Built-in Variants

Tailwind includes many variants out of the box:

Pseudo-class Variants

<button class="hover:bg-blue-600 focus:ring-2 active:scale-95">
  Interactive button
</button>

Pseudo-element Variants

<p class="first-line:uppercase first-letter:text-7xl">
  Styled text
</p>

<div class="before:content-['→'] after:content-['←']">
  Content with pseudo-elements
</div>

<input class="placeholder:italic placeholder:text-gray-400" placeholder="Search..." />

Media Query Variants

<div class="text-sm md:text-base lg:text-lg">
  Responsive text
</div>

Variant Categories

State Variants

hover
pseudo-class
Applied on hover (with @media (hover: hover) wrapper)
<button class="hover:bg-blue-600">Hover me</button>
focus
pseudo-class
Applied when element has focus
<input class="focus:ring-2 focus:ring-blue-500" />
active
pseudo-class
Applied when element is being activated
<button class="active:scale-95">Press me</button>
visited
pseudo-class
Applied to visited links
<a class="visited:text-purple-600">Link</a>
disabled
pseudo-class
Applied when element is disabled
<button class="disabled:opacity-50" disabled>Disabled</button>

Form Variants

<!-- Validation states -->
<input class="valid:border-green-500 invalid:border-red-500" />

<!-- Checked states -->
<input type="checkbox" class="checked:bg-blue-600" />

<!-- Indeterminate state -->
<input type="checkbox" class="indeterminate:bg-gray-600" />

<!-- Required/Optional -->
<input class="required:border-red-500 optional:border-gray-300" />

Positional Variants

<ul>
  <li class="first:pt-0">First item</li>
  <li class="last:pb-0">Last item</li>
  <li class="only:border-0">Only item</li>
  <li class="odd:bg-gray-100">Odd items</li>
  <li class="even:bg-white">Even items</li>
</ul>

Group & Peer Variants

Apply styles based on parent or sibling state:
<div class="group">
  <img class="group-hover:opacity-75" />
  <h3 class="group-hover:text-blue-600">Title</h3>
</div>

Responsive Variants

The default breakpoints:
--breakpoint-sm: 40rem   /* 640px */
--breakpoint-md: 48rem   /* 768px */
--breakpoint-lg: 64rem   /* 1024px */
--breakpoint-xl: 80rem   /* 1280px */
--breakpoint-2xl: 96rem  /* 1536px */
Usage:
<div class="text-sm sm:text-base md:text-lg lg:text-xl">
  Responsive sizing
</div>

Custom Variants with Plugins

Create custom variants using the plugin API:

Static Variant

Add a simple static variant:
tailwind.config.ts
import type { Config } from 'tailwindcss'
import plugin from 'tailwindcss/plugin'

export default {
  plugins: [
    plugin(({ addVariant }) => {
      // Add a `third` variant
      addVariant('third', '&:nth-child(3)')
      
      // Add a `hocus` variant (hover or focus)
      addVariant('hocus', ['&:hover', '&:focus'])
      
      // Add an `optional` variant for optional form elements
      addVariant('optional', '&:optional')
      
      // Add a `supports-grid` variant
      addVariant('supports-grid', '@supports (display: grid)')
    }),
  ],
} satisfies Config
Usage:
<div class="third:bg-blue-500">Third child is blue</div>
<button class="hocus:ring-2">Hover or focus for ring</button>

Functional Variant

Create variants that accept values:
import plugin from 'tailwindcss/plugin'

export default {
  plugins: [
    plugin(({ matchVariant }) => {
      // Add an `nth` variant
      matchVariant(
        'nth',
        (value) => `&:nth-child(${value})`,
      )
      
      // Add a `data` variant with values
      matchVariant(
        'data',
        (value) => `&[data-${value}]`,
        {
          values: {
            checked: 'checked',
            active: 'active',
            disabled: 'disabled',
          },
        },
      )
    }),
  ],
}
Usage:
<div class="nth-3:bg-blue-500">Third child</div>
<div class="nth-[2n]:bg-gray-100">Even children</div>
<div class="data-active:bg-green-500" data-active>Active state</div>

Compound Variants

Create variants that modify other variants:
import plugin from 'tailwindcss/plugin'

export default {
  plugins: [
    plugin(({ addVariant }) => {
      // Add a custom group variant
      addVariant('group-optional', ':merge(.group):optional &')
      
      // Add a custom peer variant  
      addVariant('peer-optional', ':merge(.peer):optional ~ &')
    }),
  ],
}

Functional Variants

Some variants accept arbitrary values:
<div class="nth-3:bg-blue-500">3rd child</div>
<div class="nth-[2n+1]:bg-gray-100">Odd children</div>

Stacking Variants

Combine multiple variants:
<!-- Responsive + hover -->
<button class="md:hover:bg-blue-600">Desktop hover</button>

<!-- Dark mode + hover + focus -->
<button class="dark:hover:focus:ring-4">Complex state</button>

<!-- Group + responsive + hover -->
<div class="group-hover:md:scale-110">Scales on group hover (desktop)</div>

Variant Order

Variants are applied in a specific order (roughly):
  1. Responsive variants (sm:, md:, etc.)
  2. State variants (hover:, focus:, etc.)
  3. Compound variants (group-*:, peer-*:)
  4. Pseudo-elements (before:, after:, etc.)
The order of variants in your class name doesn’t matter - Tailwind handles ordering automatically based on CSS specificity rules.

Advanced Variant Examples

Custom Dark Mode

Implement a custom dark mode strategy:
export default {
  darkMode: ['variant', '&:is(.dark *)'],
}

RTL Support

import plugin from 'tailwindcss/plugin'

export default {
  plugins: [
    plugin(({ addVariant }) => {
      addVariant('rtl', '&:is([dir="rtl"] *)')
      addVariant('ltr', '&:is([dir="ltr"] *)')
    }),
  ],
}
Usage:
<div class="ml-4 rtl:mr-4 rtl:ml-0">
  RTL-aware margin
</div>

Container Queries

Use container query variants:
<div class="@container">
  <div class="@md:text-lg @lg:text-xl">
    Responsive to container size
  </div>
</div>

Not Variant

Negate other variants:
<div class="not-first:mt-4">Margin except first</div>
<div class="not-last:border-b">Border except last</div>
<div class="not-hover:opacity-75">Opacity when not hovered</div>

Variant Modifiers

Some variants support modifiers:
<!-- Named group -->
<div class="group/card">
  <div class="group-hover/card:opacity-100">
    Only affected by .group/card
  </div>
</div>

<!-- Named peer -->
<input class="peer/email" type="email" />
<span class="peer-invalid/email:visible">Invalid email</span>

Best Practices

  1. Use semantic variants - hover:, focus:, etc. are more maintainable than arbitrary selectors
  2. Leverage responsive variants - Build mobile-first with progressive enhancement
  3. Group related states - Use group and peer for parent/sibling relationships
  4. Be mindful of specificity - More variants = higher specificity
Stacking too many variants can create overly specific selectors that are hard to override. Consider extracting complex variant combinations into component classes.

Build docs developers (and LLMs) love