css-variants is no longer maintained. Use @lynstack/class-recipe: faster, stricter types, and React Native support. Migrate →
No longer maintained. Migrate to class-recipe →
css-variants is no longer maintained. Use @lynstack/class-recipe: faster, stricter types, and React Native support. Migrate →
No longer maintained. Migrate to class-recipe →
CSS variant libraries are JavaScript/TypeScript tools that help developers manage component styling through a variant-based API. Instead of writing complex conditional class logic, you define variants (like color, size, disabled) and the library generates the correct CSS classes or styles at runtime.
Popular CSS variant libraries include:
| Feature | css-variants | CVA | tailwind-variants |
|---|---|---|---|
| Bundle Size | ~1KB | ~2KB | ~5KB |
| Dependencies | 0 | 1 (clsx) | 1 (tailwind-merge) |
| TypeScript | Full inference | Full inference | Full inference |
| Single-element variants | cv() | cva() | tv() |
| Multi-slot components | scv() | ❌ Not built-in | tv() with slots |
| Inline style variants | sv() / ssv() | ❌ Not built-in | ❌ Not built-in |
| Works without Tailwind | ✅ | ✅ | ✅ |
css-variants is engineered for performance. Here’s how it compares:
| Scenario | css-variants | CVA | Winner |
|---|---|---|---|
| Base class only | 23.2M ops/s | 21.2M ops/s | css-variants (1.1x) |
| Compound variants (no match) | 4.2M ops/s | 1.1M ops/s | css-variants (3.7x) |
| Compound variants (match) | 4.1M ops/s | 1.0M ops/s | css-variants (3.9x) |
| Complex component | 5.1M ops/s | 0.7M ops/s | css-variants (6.9x) |
| Scenario | css-variants | tailwind-variants | Winner |
|---|---|---|---|
| Base class only | 23.1M ops/s | 6.4M ops/s | css-variants (3.6x) |
| Single variant | 9.9M ops/s | 2.2M ops/s | css-variants (4.5x) |
| Compound variants | 4.0M ops/s | 0.7M ops/s | css-variants (5.4x) |
| Complex component | 5.2M ops/s | 0.5M ops/s | css-variants (11x) |
| Slot-based component | 1.6M ops/s | 0.3M ops/s | css-variants (6.3x) |
Multi-slot (or multi-element) components like cards, modals, and dropdowns need styles for multiple parts (root, header, body, footer, etc.).
import { scv } from 'css-variants'
const card = scv({ slots: ['root', 'header', 'body', 'footer'], base: { root: 'rounded-lg border', header: 'p-4 border-b', body: 'p-4', footer: 'p-4 border-t', }, variants: { variant: { default: { root: 'border-gray-200' }, primary: { root: 'border-blue-500' }, }, },})
// Returns object with class strings directlyconst classes = card({ variant: 'primary' })// classes.root, classes.header, classes.body, classes.footer// CVA doesn't have built-in slot support// You need to create separate cva() calls for each slot
import { cva } from 'class-variance-authority'
const cardRoot = cva('rounded-lg border', { variants: { variant: { default: 'border-gray-200', primary: 'border-blue-500', }, },})
const cardHeader = cva('p-4 border-b')const cardBody = cva('p-4')const cardFooter = cva('p-4 border-t')
// Must call each function separatelyimport { tv } from 'tailwind-variants'
const card = tv({ slots: { root: 'rounded-lg border', header: 'p-4 border-b', body: 'p-4', footer: 'p-4 border-t', }, variants: { variant: { default: { root: 'border-gray-200' }, primary: { root: 'border-blue-500' }, }, },})
// Returns functions that must be calledconst { root, header, body, footer } = card({ variant: 'primary' })// root(), header(), body(), footer() — extra function calls neededcss-variants is the only library with built-in support for inline CSS style variants — useful for dynamic values, CSS custom properties, or React’s style prop.
import { sv } from 'css-variants'
const box = sv({ base: { display: 'flex', borderRadius: '8px' }, variants: { size: { sm: { padding: '8px', width: '100px' }, lg: { padding: '24px', width: '300px' }, }, },})
// Returns a style object, not class namesbox({ size: 'lg' })// => { display: 'flex', borderRadius: '8px', padding: '24px', width: '300px' }Use cases for style variants:
sv and ssv functions)extend property for inheritance)Already using another library? Migration is straightforward:
base propertyscv for slots, add classNameResolver for tw-mergeYes. css-variants is a drop-in alternative to CVA (Class Variance Authority) with a nearly identical API. The main differences are:
base property instead of the first argumentclassName instead of class in compound variantsscv), style variants (sv, ssv)Yes. css-variants can replace tailwind-variants with better performance and smaller bundle size. Key differences:
cv for single-element, scv for slots (instead of tv for both)classNameResolver if needed)extend property (use object spreading for composition)Absolutely yes. css-variants is CSS-framework agnostic. It works with:
| Function | Output | Use Case |
|---|---|---|
cv() | Class string | Single-element components |
scv() | Object of class strings | Multi-slot components (card, modal) |
sv() | Style object | Single-element inline styles |
ssv() | Object of style objects | Multi-slot inline styles |
css-variants is a modern, performance-focused CSS variant library that serves as an excellent alternative to both CVA and tailwind-variants. It offers:
→ Ready to start? Get started with css-variants