UNPKG

css-variants

Version:

A lightweight, flexible API for managing CSS class variants.

377 lines (300 loc) â€Ē 7.53 kB
[![test](https://github.com/timphandev/css-variants/actions/workflows/ci.yml/badge.svg)](https://github.com/timphandev/css-variants/actions/workflows/ci.yml) [![license](https://img.shields.io/github/license/timphandev/css-variants)](https://github.com/timphandev/css-variants/blob/main/LICENSE) [![npm](https://img.shields.io/npm/dm/css-variants)](https://npmjs.com/package/css-variants) ![npm](https://img.shields.io/npm/v/css-variants) # 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 ```bash # 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-cv) - Class Variants for managing single component class names - [sv](#style-variants-sv) - Style Variants for managing single component inline styles - [scv](#slot-class-variants-scv) - Slot Class Variants for managing multiple slot class names - [ssv](#slot-style-variants-ssv) - Slot Style Variants for managing multiple slot inline styles - [cx](#class-merger-cx) - Utility for composing class names conditionally. ### Class Variants ([cv](./src/cv.ts)) For managing class names for a single component: ```ts 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](./src/sv.ts)) For managing inline styles: ```ts 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](./src/scv.ts)) For managing class names across multiple slots/elements: ```ts 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](./src/ssv.ts)) For managing inline styles across multiple slots: ```ts 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](./src/cx.ts)) Similar to `clsx/classnames` but with better TypeScript support. ```tsx 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: ```ts 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: ```ts 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: ```ts 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: ```ts 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: ```ts 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: - [CVA (Class Variance Authority)](https://github.com/joe-bell/cva) - [Panda CSS](https://github.com/chakra-ui/panda) ## Contribute If you would like to contribute to the project, please read how to contribute here [CONTRIBUTING.md](./CONTRIBUTING.md). ## License Licensed under the MIT License. See [MIT license](./LICENSE) for more information.