---
title: "Variants"
description: "Create multiple versions of a component with variant axes, boolean flags, compound rules, and defaults."
canonical_url: "https://vinicunca.dev/unocss-variants/concepts/variants"
---
# Variants

> Create multiple versions of a component with variant axes, boolean flags, compound rules, and defaults.

## Adding variants

Add axes under `variants`. There is no limit to how many you define. Each value is a class string, an array of classes, or — with [slots](https://vinicunca.dev/unocss-variants/concepts/slots) — a map of slot names.

```vue
<template>
  <uv-button :uv="{
    base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
    variants: {
      color: {
        primary: 'bg-blue-500 hover:bg-blue-700',
        secondary: 'bg-purple-500 hover:bg-purple-700',
        success: 'bg-green-500 hover:bg-green-700'
      }
    }
  }" as="button" color="secondary">
    <p>
      Button
    </p>
  </uv-button>
</template>
```

```ts
import { uv } from 'unocss-variants';

const button = uv({
  base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    }
  }
});

button({ color: 'secondary' });
// font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80 bg-purple-500 hover:bg-purple-700
```

> [!NOTE]
> 
> You can also pass arrays: `base: ['font-semibold', 'color-white', ...]`.

## Multiple variants

Axes combine independently.

```vue
<template>
  <uv-multi-button />
</template>
```

```ts
import { uv } from 'unocss-variants';

const button = uv({
  base: 'font-semibold color-white rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    },
    size: {
      sm: 'py-1 px-3 text-xs',
      md: 'py-1.5 px-4 text-sm',
      lg: 'py-2 px-6 text-md'
    }
  }
});

button({ color: 'primary', size: 'sm' });
// font-semibold color-white rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700 py-1 px-3 text-xs

button({ color: 'secondary', size: 'md' });
// font-semibold color-white rounded-full active:opacity-80 bg-purple-500 hover:bg-purple-700 py-1.5 px-4 text-sm

button({ color: 'success', size: 'lg' });
// font-semibold color-white rounded-full active:opacity-80 bg-green-500 hover:bg-green-700 py-2 px-6 text-md
```

## Boolean variants

Use `'true'` / `'false'` keys (or `true` / `false` in JS) for flags such as `disabled`. Passing `disabled: true` selects the `true` entry.

```vue
<template>
  <uv-button :uv="{
    base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
    variants: {
      color: {
        primary: 'bg-blue-500 hover:bg-blue-700',
        secondary: 'bg-purple-500 hover:bg-purple-700',
        success: 'bg-green-500 hover:bg-green-700'
      },
      disabled: {
        true: 'opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none'
      }
    }
  }" as="button" color="secondary" :disabled="true">
    <p>
      Disabled
    </p>
  </uv-button>
</template>
```

```ts
import { uv } from 'unocss-variants';

const button = uv({
  base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    },
    disabled: {
      true: 'opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none'
    }
  }
});

button({ color: 'secondary', disabled: true });
// font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80 bg-purple-500 hover:bg-purple-700 opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none
```

`uno-layer-uv:bg-gray-500` is what makes the gray background win over `bg-purple-500`. Without the layer prefix, both background utilities stay in the string and stylesheet order decides the winner.

## Compound variants

`compoundVariants` is an **array of objects**. Each object lists the variant values that must match, plus `class` or `className` to append when they do.

```vue
<template>
  <uv-button :uv="{
    base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
    variants: {
      color: {
        primary: 'bg-blue-500 hover:bg-blue-700',
        secondary: 'bg-purple-500 hover:bg-purple-700',
        success: 'bg-green-500 hover:bg-green-700'
      },
      disabled: {
        true: 'opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none'
      }
    },
    compoundVariants: [
      {
        color: 'success',
        disabled: true,
        class: 'uno-layer-uv:bg-green-100 uno-layer-uv:color-green-700 dark:color-green-800'
      }
    ]
  }" as="button" color="success" :disabled="true">
    <p>
      Disabled
    </p>
  </uv-button>
</template>
```

```ts
import { uv } from 'unocss-variants';

const button = uv({
  base: 'font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    },
    disabled: {
      true: 'opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none'
    }
  },
  compoundVariants: [
    {
      color: 'success',
      disabled: true,
      class: 'uno-layer-uv:bg-green-100 uno-layer-uv:color-green-700 dark:color-green-800'
    }
  ]
});

button({ color: 'success', disabled: true });
// font-semibold color-white text-sm py-1 px-4 rounded-full active:opacity-80 bg-green-500 hover:bg-green-700 opacity-50 uno-layer-uv:bg-gray-500 pointer-events-none uno-layer-uv:bg-green-100 uno-layer-uv:color-green-700 dark:color-green-800
```

> [!WARNING]
> 
> `compoundVariants` must be an array. Passing a plain object throws at call time.

### Match several values

Use an array on a compound key to match any of those values.

```ts
const button = uv({
  base: 'font-semibold color-white text-md py-1.5 px-4 rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    },
    disabled: {
      true: 'opacity-50 bg-gray-500 pointer-events-none'
    }
  },
  compoundVariants: [
    {
      color: 'success',
      disabled: true,
      class: 'bg-green-100 color-green-700 dark:color-green-800'
    },
    {
      color: ['primary', 'secondary'],
      disabled: true,
      class: 'color-slate-400 uno-layer-uv:bg-slate-200 uno-layer-uv:dark:bg-slate-800 opacity-100'
    }
  ]
});

button({ color: 'primary', disabled: true });
// font-semibold color-white text-md py-1.5 px-4 rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700 opacity-50 bg-gray-500 pointer-events-none color-slate-400 uno-layer-uv:bg-slate-200 uno-layer-uv:dark:bg-slate-800 opacity-100

button({ color: 'secondary', disabled: true });
// font-semibold color-white text-md py-1.5 px-4 rounded-full active:opacity-80 bg-purple-500 hover:bg-purple-700 opacity-50 bg-gray-500 pointer-events-none color-slate-400 uno-layer-uv:bg-slate-200 uno-layer-uv:dark:bg-slate-800 opacity-100
```

## Default variants

`defaultVariants` supplies values when a key is omitted at the call site. Keys listed here stay optional on `VariantProps`.

```vue
<template>
  <uv-button :uv="{
    base: 'font-semibold color-white py-1 px-3 rounded-full active:opacity-80',
    variants: {
      color: {
        primary: 'bg-blue-500 hover:bg-blue-700',
        secondary: 'bg-purple-500 hover:bg-purple-700',
        success: 'bg-green-500 hover:bg-green-700'
      },
      size: {
        sm: 'py-1 px-3 text-sm',
        md: 'py-1.5 px-4 text-md',
        lg: 'py-2 px-6 text-lg'
      }
    },
    defaultVariants: {
      color: 'primary',
      size: 'md'
    }
  }" as="button">
    <p>
      Default
    </p>
  </uv-button>
</template>
```

```ts
import { uv } from 'unocss-variants';

const button = uv({
  base: 'font-semibold color-white rounded-full active:opacity-80',
  variants: {
    color: {
      primary: 'bg-blue-500 hover:bg-blue-700',
      secondary: 'bg-purple-500 hover:bg-purple-700',
      success: 'bg-green-500 hover:bg-green-700'
    },
    size: {
      sm: 'py-1 px-3 text-sm',
      md: 'py-1.5 px-4 text-md',
      lg: 'py-2 px-6 text-lg'
    }
  },
  defaultVariants: {
    color: 'primary',
    size: 'md'
  }
});

button();
// font-semibold color-white rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700 py-1.5 px-4 text-md
```

Passing `size: 'sm'` overrides the default for that call only.


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
