> ## 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

> Create custom variants with addVariant() and matchVariant() to modify utilities based on state or conditions

## addVariant()

Register a static variant that modifies how utilities are applied.

<ParamField path="name" type="string" required>
  The variant name (must be alphanumeric, lowercase, with dashes or underscores)
</ParamField>

<ParamField path="variant" type="string | string[] | CssInJs" required>
  Selector transformation, media query, or CSS-in-JS object defining the variant behavior
</ParamField>

### String Selector

Use `&` as a placeholder for the utility selector:

```javascript theme={null}
import plugin from 'tailwindcss/plugin'

export default plugin(function({ addVariant }) {
  addVariant('hocus', '&:hover, &:focus')
})
```

```html theme={null}
<button class="hocus:bg-blue-500">
  Hover or Focus
</button>
```

Compiles to:

```css theme={null}
.hocus\\:bg-blue-500:hover,
.hocus\\:bg-blue-500:focus {
  background-color: #3b82f6;
}
```

### Array of Selectors

```javascript theme={null}
addVariant('hocus', ['&:hover', '&:focus'])
```

### Object Syntax with @slot

Use `@slot` to mark where the utility styles should be inserted:

```javascript theme={null}
addVariant('hocus', {
  '&:hover': '@slot',
  '&:focus': '@slot'
})
```

### Media Queries

```javascript theme={null}
addVariant('tablet', '@media (min-width: 768px) and (max-width: 1024px)')
```

```html theme={null}
<div class="tablet:grid-cols-2">
```

### Complex Variants with Nesting

```javascript theme={null}
addVariant('hocus-within', {
  '@media (hover: hover)': {
    '&:hover': '@slot'
  },
  '&:focus-within': '@slot'
})
```

### At-Rules and Supports

```javascript theme={null}
addVariant('supports-grid', '@supports (display: grid)')
```

```javascript theme={null}
addVariant('reduced-motion', '@media (prefers-reduced-motion: reduce)')
```

### Advanced Example

```javascript theme={null}
import plugin from 'tailwindcss/plugin'

export default plugin(function({ addVariant }) {
  // Child selector
  addVariant('child', '& > *')
  
  // Optional form fields
  addVariant('optional', '&:optional')
  
  // RTL support
  addVariant('rtl', '[dir="rtl"] &')
  
  // Print styles
  addVariant('print', '@media print')
})
```

## matchVariant()

Register a dynamic variant that accepts values.

<ParamField path="name" type="string" required>
  The variant name
</ParamField>

<ParamField path="callback" type="(value: string, extra: { modifier: string | null }) => string | string[]" required>
  Function that returns selector transformation(s) based on the value
</ParamField>

<ParamField path="options" type="object">
  Configuration for values and sorting

  <ParamField path="options.values" type="Record<string, T>">
    Named values that can be used with the variant
  </ParamField>

  <ParamField path="options.sort" type="(a, b) => number">
    Custom sorting function for variant order
  </ParamField>
</ParamField>

### Basic Example

```javascript theme={null}
import plugin from 'tailwindcss/plugin'

export default plugin(function({ matchVariant }) {
  matchVariant('nth', (value) => `&:nth-child(${value})`)
})
```

```html theme={null}
<div class="nth-[2]:bg-blue-500">
<div class="nth-[odd]:bg-gray-100">
<div class="nth-[3n+1]:bg-red-500">
```

### With Named Values

```javascript theme={null}
matchVariant(
  'supports',
  (value) => `@supports (${value})`,
  {
    values: {
      grid: 'display: grid',
      flex: 'display: flex',
      sticky: 'position: sticky'
    }
  }
)
```

```html theme={null}
<div class="supports-grid:grid">
<div class="supports-flex:flex">
<div class="supports-[transform]:rotate-45">
```

### Data Attributes

```javascript theme={null}
matchVariant('data', (value) => `&[data-${value}]`)
```

```html theme={null}
<div class="data-[state=active]:bg-blue-500">
<div class="data-[disabled]:opacity-50">
```

### ARIA States

```javascript theme={null}
matchVariant('aria', (value) => `&[aria-${value}]`)
```

```html theme={null}
<button class="aria-[pressed=true]:bg-blue-600">
<div class="aria-[expanded]:rotate-180">
```

### Screen Variants

```javascript theme={null}
import plugin from 'tailwindcss/plugin'

export default plugin(function({ matchVariant }) {
  matchVariant(
    'max',
    (value) => `@media (max-width: ${value})`,
    {
      values: {
        sm: '640px',
        md: '768px',
        lg: '1024px',
        xl: '1280px'
      }
    }
  )
})
```

```html theme={null}
<div class="max-sm:hidden">
<div class="max-md:text-sm">
<div class="max-[600px]:hidden">
```

### With Modifiers

```javascript theme={null}
matchVariant(
  'tooltip',
  (value, { modifier }) => {
    return `&[data-tooltip="${value}"]${modifier ? `[data-position="${modifier}"]` : ''}`
  },
  {
    values: {
      info: 'info',
      warning: 'warning',
      error: 'error'
    }
  }
)
```

### Group and Peer Variants

```javascript theme={null}
matchVariant('group', (value) => `:merge(.group\\/${value}) &`)
```

In v4, `group-*` and `peer-*` variants compound automatically, so you don't need to use `:merge()` anymore.

### Custom Sorting

```javascript theme={null}
matchVariant(
  'min',
  (value) => `@media (min-width: ${value})`,
  {
    values: {
      sm: '640px',
      md: '768px',
      lg: '1024px'
    },
    sort(a, b) {
      // Parse pixel values and sort numerically
      const aValue = parseInt(a.value)
      const bValue = parseInt(b.value)
      return aValue - bValue
    }
  }
)
```

## Variant Compounding

Variants automatically work with `group-*` and `peer-*` patterns:

```javascript theme={null}
addVariant('optional', '&:optional')
```

```html theme={null}
<div class="group">
  <input class="group-optional:block" />
</div>

<input class="peer" />
<div class="peer-optional:hidden"></div>
```

## Stacking Variants

Variants can be stacked in any order:

```html theme={null}
<button class="hover:focus:dark:md:bg-blue-500">
```

Variants are applied in a specific order for consistent behavior. Custom variants follow Tailwind's variant ordering system.

## Validation

<Note>
  Variant names must:

  * Start with a lowercase letter or number
  * Contain only alphanumeric characters, dashes, or underscores
  * Not include `:merge()` in v4 (automatically handled)
</Note>

Invalid examples:

```javascript theme={null}
// ❌ Invalid: starts with uppercase
addVariant('Hover', '&:hover')

// ❌ Invalid: contains special characters
addVariant('hover!', '&:hover')

// ✅ Valid
addVariant('hover', '&:hover')
addVariant('hover2', '&:hover')
addVariant('hover-state', '&:hover')
addVariant('hover_state', '&:hover')
```

## Migration from v3

In v4, the `:merge()` pseudo-class is no longer needed. Variants automatically compound:

```javascript theme={null}
// v3
addVariant('group-optional', ':merge(.group):optional &')

// v4 (`:merge()` is ignored)
addVariant('group-optional', '.group:optional &')
```

<Note>
  All built-in Tailwind variants (hover, focus, dark, responsive, etc.) work automatically with custom utilities. You only need to register custom variants for new interaction patterns.
</Note>
