css-variants
v2.1.0
Published
A lightweight, flexible API for managing CSS class variants.
Downloads
8,666
Maintainers
Readme
css-variants
A lightweight, flexible API for managing CSS class variants and inline styles in JavaScript and TypeScript projects.
Features
- 🎨 Dynamic class name generation with variant support
- 💅 First-class inline styles support alongside class names
- 🧩 Powerful slot-based variant system for complex components
- 📦 Zero dependencies
- 🔒 Fully type-safe with TypeScript
- 🚀 Framework-agnostic
- 🪶 Lightweight (~2KB gzipped)
- 🔥 Strong tree-shaking support
- ⚡️ Optimal runtime performance
Installation
# npm
npm install css-variants
# yarn
yarn add css-variants
# pnpm
pnpm add css-variants
Core Utilities
css-variants provides four main utilities:
- cv - Class Variants for managing single component class names
- sv - Style Variants for managing single component inline styles
- scv - Slot Class Variants for managing multiple slot class names
- ssv - Slot Style Variants for managing multiple slot inline styles
- cx - Utility for composing class names conditionally.
Class Variants (cv)
For managing class names for a single component:
import { cv } from 'css-variants'
const button = cv({
base: 'font-bold rounded-lg',
variants: {
color: {
primary: 'bg-blue-500 text-white',
secondary: 'bg-gray-500 text-white'
},
size: {
sm: 'text-sm px-2 py-1',
lg: 'text-lg px-4 py-2'
}
},
compoundVariants: [
{
color: 'primary',
size: 'lg',
className: 'uppercase'
}
],
defaultVariants: {
color: 'primary',
size: 'sm'
}
})
// Usage
button() // => 'font-bold rounded-lg bg-blue-500 text-white text-sm px-2 py-1'
button({ size: 'lg' }) // => 'font-bold rounded-lg bg-blue-500 text-white text-lg px-4 py-2 uppercase'
button({ size: 'lg', className: 'custom' }) // => 'font-bold rounded-lg bg-blue-500 text-white text-lg px-4 py-2 uppercase custom'
Style Variants (sv)
For managing inline styles:
import { sv } from 'css-variants'
const button = sv({
base: {
fontWeight: 'bold',
borderRadius: '8px'
},
variants: {
color: {
primary: {
backgroundColor: 'blue',
color: 'white'
},
secondary: {
backgroundColor: 'gray',
color: 'white'
}
}
}
})
// Usage
button({ color: 'primary' })
// => { fontWeight: 'bold', borderRadius: '8px', backgroundColor: 'blue', color: 'white' }
button({
color: 'secondary',
style: { padding: '4px' },
})
// => { fontWeight: 'bold', borderRadius: '8px', backgroundColor: 'gray', color: 'white', padding: '4px' }
Slot Class Variants (scv)
For managing class names across multiple slots/elements:
import { scv } from 'css-variants'
const card = scv({
slots: ['root', 'title', 'content'],
base: {
root: 'rounded-lg shadow',
title: 'text-xl font-bold',
content: 'mt-2'
},
variants: {
size: {
sm: {
root: 'p-4',
title: 'text-base'
},
lg: {
root: 'p-6',
title: 'text-2xl'
}
}
}
})
// Usage
card({ size: 'sm' })
// => {
// root: 'rounded-lg shadow p-4',
// title: 'text-xl font-bold text-base',
// content: 'mt-2'
// }
card({
size: 'lg',
classNames: {
content: 'custom',
},
})
// => {
// root: 'rounded-lg shadow p-6',
// title: 'text-xl font-bold text-2xl',
// content: 'mt-2 custom'
// }
Slot Style Variants (ssv)
For managing inline styles across multiple slots:
import { ssv } from 'css-variants'
const card = ssv({
slots: ['root', 'title'],
base: {
root: { padding: '1rem' },
title: { fontWeight: 'bold' }
},
variants: {
size: {
sm: {
root: { maxWidth: '300px' },
title: { fontSize: '14px' }
},
lg: {
root: { maxWidth: '600px' },
title: { fontSize: '18px' }
}
}
}
})
// Usage
card({ size: 'sm' })
// => {
// root: { padding: '1rem', maxWidth: '300px' },
// title: { fontWeight: 'bold', fontSize: '14px' }
// }
card({
size: 'lg',
styles: {
title: {
color: 'red',
},
},
})
// => {
// root: { padding: '1rem', maxWidth: '600px' },
// title: { fontWeight: 'bold', fontSize: '18px', color: 'red' }
// }
Class Merger (cx)
Similar to clsx/classnames
but with better TypeScript support.
import { cx } from 'css-variants'
// Basic usage
cx('foo', 'bar') // => 'foo bar'
// With conditions
cx('foo', {
'bar': true,
'baz': false
}) // => 'foo bar'
// With arrays
cx('foo', ['bar', 'baz']) // => 'foo bar baz'
// With nested structures
cx('foo', {
bar: true,
baz: [
'qux',
{ quux: true }
]
}) // => 'foo bar qux quux'
// With falsy values (they're ignored)
cx('foo', null, undefined, false, 0, '') // => 'foo'
Advanced Features
Boolean Variants
Support for boolean variants to toggle styles conditionally:
import { cv } from 'css-variants'
const button = cv({
variants: {
disabled: {
true: 'opacity-50 cursor-not-allowed'
}
}
})
button({ disabled: true }) // => 'opacity-50 cursor-not-allowed'
Compound Variants
Apply styles based on combinations of variants:
import { cv } from 'css-variants'
const button = cv({
variants: {
color: {
primary: 'bg-blue-500',
danger: 'bg-red-500'
},
size: {
sm: 'text-sm',
lg: 'text-lg'
}
},
compoundVariants: [
{
color: 'danger',
size: 'lg',
className: 'animate-pulse'
}
]
})
button({ color: 'danger', size: 'lg' }) // => 'bg-red-500 text-lg animate-pulse'
Default Variants
Specify default variant values:
import { cv } from 'css-variants'
const button = cv({
variants: {
size: {
sm: 'text-sm',
lg: 'text-lg'
}
},
defaultVariants: {
size: 'sm'
}
})
button() // => 'text-sm'
Custom Class Name Resolver
Use your preferred class name utility:
import { cv } from 'css-variants'
import { clsx } from 'clsx'
const button = cv({
base: 'btn',
variants: {
color: {
primary: 'btn-primary'
}
},
classNameResolver: clsx
})
TypeScript Support
Full TypeScript support with automatic type inference:
import { cv } from 'css-variants'
const button = cv({
variants: {
size: {
sm: 'text-sm',
lg: 'text-lg'
}
}
})
type ButtonProps = Parameters<typeof button>[0]
// => { size?: 'sm' | 'lg' | undefined }
Inspiration
This library is inspired by several excellent projects:
Contribute
If you would like to contribute to the project, please read how to contribute here CONTRIBUTING.md.
License
Licensed under the MIT License.
See MIT license for more information.