Migration from Tailwind Variants (tailwind-variants)
css-variants provides a similar API to Tailwind Variants, making migration straightforward. Both libraries support slots, variants, and compound variants.
Key Differences
Section titled “Key Differences”| Feature | Tailwind Variants | css-variants |
|---|---|---|
| Function name | tv | cv (single) / scv (slots) |
| Slots definition | slots object in tv | Separate scv function |
| Compound class key | class | className |
| Compound slots | compoundSlots | Use compoundVariants with slot keys |
| Built-in tw-merge | Yes (default) | No (use classNameResolver) |
| Component composition | extend property | Not built-in |
| Style variants | Not built-in | sv and ssv functions |
Basic Migration (No Slots)
Section titled “Basic Migration (No Slots)”import { tv } from 'tailwind-variants'
const button = tv({ base: 'font-semibold rounded-lg transition-colors', variants: { color: { primary: 'bg-blue-600 text-white', secondary: 'bg-gray-200 text-gray-900', }, size: { sm: 'px-3 py-1 text-sm', lg: 'px-6 py-3 text-lg', }, }, defaultVariants: { color: 'primary', size: 'sm', },})import { cv } from 'css-variants'
const button = cv({ base: 'font-semibold rounded-lg transition-colors', variants: { color: { primary: 'bg-blue-600 text-white', secondary: 'bg-gray-200 text-gray-900', }, size: { sm: 'px-3 py-1 text-sm', lg: 'px-6 py-3 text-lg', }, }, defaultVariants: { color: 'primary', size: 'sm', },})Compound Variants
Section titled “Compound Variants”const button = tv({ base: 'btn', variants: { color: { primary: '...', secondary: '...' }, size: { sm: '...', lg: '...' }, }, compoundVariants: [ { color: 'primary', size: 'lg', class: 'shadow-lg font-bold', // TV uses 'class' }, ],})const button = cv({ base: 'btn', variants: { color: { primary: '...', secondary: '...' }, size: { sm: '...', lg: '...' }, }, compoundVariants: [ { color: 'primary', size: 'lg', className: 'shadow-lg font-bold', // css-variants uses 'className' }, ],})Slots Migration
Section titled “Slots Migration”In Tailwind Variants, slots are defined within the tv function. In css-variants, use the separate scv function for multi-element components.
import { tv } from 'tailwind-variants'
const card = tv({ slots: { root: 'rounded-lg border bg-white', header: 'border-b p-4 font-semibold', content: 'p-4', footer: 'border-t p-4', }, variants: { variant: { default: { root: 'border-gray-200', }, primary: { root: 'border-blue-200', header: 'bg-blue-50', }, }, }, defaultVariants: { variant: 'default', },})
// Usageconst { root, header, content, footer } = card({ variant: 'primary' })import { scv } from 'css-variants'
const card = scv({ slots: ['root', 'header', 'content', 'footer'], // Define slot names as array base: { root: 'rounded-lg border bg-white', header: 'border-b p-4 font-semibold', content: 'p-4', footer: 'border-t p-4', }, variants: { variant: { default: { root: 'border-gray-200', }, primary: { root: 'border-blue-200', header: 'bg-blue-50', }, }, }, defaultVariants: { variant: 'default', },})
// Usage - returns object with class strings directlyconst classes = card({ variant: 'primary' })// classes.root, classes.header, etc.Compound Slots
Section titled “Compound Slots”Tailwind Variants has a dedicated compoundSlots feature. In css-variants, achieve the same result using compoundVariants with slot-specific classes.
const card = tv({ slots: { root: 'rounded-lg', header: 'p-4', footer: 'p-4', }, variants: { color: { primary: {}, secondary: {}, }, }, compoundSlots: [ { slots: ['header', 'footer'], color: 'primary', class: 'bg-blue-100', }, ],})const card = scv({ slots: ['root', 'header', 'footer'], base: { root: 'rounded-lg', header: 'p-4', footer: 'p-4', }, variants: { color: { primary: {}, secondary: {}, }, }, compoundVariants: [ { color: 'primary', className: { header: 'bg-blue-100', footer: 'bg-blue-100', }, }, ],})Runtime Class Overrides
Section titled “Runtime Class Overrides”button({ color: 'primary', class: 'mt-4' })button({ color: 'primary', className: 'mt-4' })Tailwind Merge Integration
Section titled “Tailwind Merge Integration”Tailwind Variants includes tailwind-merge by default. css-variants is dependency-free but supports custom class merging via classNameResolver.
import { tv } from 'tailwind-variants'
// tw-merge is enabled by defaultconst button = tv({ base: 'p-4', variants: { size: { sm: 'p-2', // tw-merge handles p-4 vs p-2 conflict }, },})import { cv } from 'css-variants'import { twMerge } from 'tailwind-merge'
const button = cv({ base: 'p-4', variants: { size: { sm: 'p-2', }, }, classNameResolver: twMerge, // Opt-in to tw-merge})Type Extraction
Section titled “Type Extraction”import { tv, type VariantProps } from 'tailwind-variants'
const button = tv({ /* ... */ })
type ButtonProps = VariantProps<typeof button>import { cv } from 'css-variants'
const button = cv({ /* ... */ })
type ButtonProps = Parameters<typeof button>[0]Features Not Directly Supported
Section titled “Features Not Directly Supported”Component Composition (extend)
Section titled “Component Composition (extend)”Tailwind Variants supports extending components with the extend property. css-variants doesn’t have this built-in, but you can achieve similar results by defining shared configuration:
// Define shared configurationconst baseButtonConfig = { base: 'rounded font-medium', variants: { size: { sm: 'px-2 py-1', lg: 'px-4 py-2' }, },} as const
const baseButton = cv(baseButtonConfig)
// Extend by spreading and overridingconst primaryButton = cv({ base: [baseButtonConfig.base, 'bg-blue-600 text-white'], variants: { ...baseButtonConfig.variants, // Add or override variants },})New Features in css-variants
Section titled “New Features in css-variants”After migrating, you can take advantage of features not available in Tailwind Variants:
Style Variants (Inline CSS)
Section titled “Style Variants (Inline CSS)”import { sv } from 'css-variants'
const box = sv({ base: { display: 'flex', borderRadius: '8px' }, variants: { size: { sm: { padding: '8px' }, lg: { padding: '24px' }, }, },})
// Returns CSS style object instead of class stringbox({ size: 'lg' }) // => { display: 'flex', borderRadius: '8px', padding: '24px' }Migration Checklist
Section titled “Migration Checklist”- Replace
tvimports withcvfromcss-variants - For components with slots, use
scvinstead and defineslotsas an array - Move slot base styles from
slotsobject tobaseobject - Change
classtoclassNamein compound variants - Change
classtoclassNamein runtime overrides - Convert
compoundSlotstocompoundVariantswith slot-keyedclassName - If using tw-merge, add
classNameResolver: twMergeto your config - Update type extraction from
VariantProps<>toParameters<typeof fn>[0] - (Optional) Convert inline style components to use
svorssv