css-variants 2.0.6 → 2.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +231 -338
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,5 +1,3 @@
1
- ![Logo](.github/assets/logo.png)
2
-
3
1
  [![test](https://github.com/timphandev/css-variants/actions/workflows/ci.yml/badge.svg)](https://github.com/timphandev/css-variants/actions/workflows/ci.yml)
4
2
  [![license](https://img.shields.io/github/license/timphandev/css-variants)](https://github.com/timphandev/css-variants/blob/main/LICENSE)
5
3
  [![npm](https://img.shields.io/npm/dm/css-variants)](https://npmjs.com/package/css-variants)
@@ -7,470 +5,365 @@
7
5
 
8
6
  # css-variants
9
7
 
10
- A lightweight, flexible API for managing CSS class variants in JavaScript and TypeScript projects.
8
+ A lightweight, flexible API for managing CSS class variants and inline styles in JavaScript and TypeScript projects.
11
9
 
12
10
  ## Features
13
11
 
14
- - ðŸŽĻ Dynamic class name generation based on variants
15
- - 🔧 Support for inline styles alongside class names
16
- - ðŸ§Đ Slot-based variant system for complex components
12
+ - ðŸŽĻ Dynamic class name generation with variant support
13
+ - 💅 First-class inline styles support alongside class names
14
+ - ðŸ§Đ Powerful slot-based variant system for complex components
17
15
  - ðŸ“Ķ Zero dependencies
18
16
  - 🔒 Fully type-safe with TypeScript
19
17
  - 🚀 Framework-agnostic
20
-
21
- ## Overview
22
-
23
- `css-variants` provides a simple yet powerful way to handle dynamic class names and inline styles based on component props or state. It's designed to work seamlessly with modern JavaScript frameworks and CSS methodologies, offering a type-safe approach to styling your UI components.
24
-
25
- `css-variants` is heavily inspired by the following excellent packages:
26
-
27
- - [CVA (Class Variance Authority)](https://github.com/joe-bell/cva): A powerful utility for creating dynamic class names with variants.
28
- - [Tailwind Variants](https://github.com/nextui-org/tailwind-variants): A flexible and reusable variant system for Tailwind CSS.
29
- - [Panda CSS](https://github.com/chakra-ui/panda): A CSS-in-JS solution with a focus on developer experience and performance.
30
-
31
- Thank you to the authors and contributors of these projects for their innovative work.
32
-
33
- ## Table of Contents
34
- * [Installation](#installation)
35
- * [Variants](#variants)
36
- * [Adding variants](#adding-variants)
37
- * [Multiple variants](#multiple-variants)
38
- * [Boolean variants](#boolean-variants)
39
- * [Compound variants](#compound-variants)
40
- * [Default variants](#default-variants)
41
- * [Slots](#events)
42
- * [Basic Usage](#basic-usage)
43
- * [Slots with variants](#slots-with-variants)
44
- * [Overriding styles](#overriding-styles)
45
- * [Overriding styles on a single component](#overriding-styles-on-a-single-component)
46
- * [Overriding styles on a component with slots](#overriding-styles-on-a-component-with-slots)
47
- * [Custom function to resolve class names](#custom-function-to-resolve-class-names)
48
- * [TypeScript](#typeScript)
49
- * [Contribute](#contribute)
50
- * [License](#license)
51
-
18
+ - ðŸŠķ Lightweight (~2KB gzipped)
19
+ - ðŸ”Ĩ Strong tree-shaking support
20
+ - ⚡ïļ Optimal runtime performance
52
21
 
53
22
  ## Installation
54
23
 
55
- To use `css-variants` in your project, you can install it as a dependency:
24
+ ```bash
25
+ # npm
26
+ npm install css-variants
56
27
 
57
- ```sh
28
+ # yarn
58
29
  yarn add css-variants
30
+
31
+ # pnpm
32
+ pnpm add css-variants
59
33
  ```
60
34
 
61
- ## Variants
35
+ ## Core Utilities
62
36
 
63
- Variants allows you to create multiple versions of the same component.
37
+ css-variants provides four main utilities:
64
38
 
65
- ### Adding variants
39
+ - [cv](#class-variants-cv) - Class Variants for managing single component class names
40
+ - [sv](#style-variants-sv) - Style Variants for managing single component inline styles
41
+ - [scv](#slot-class-variants-scv) - Slot Class Variants for managing multiple slot class names
42
+ - [ssv](#slot-style-variants-ssv) - Slot Style Variants for managing multiple slot inline styles
43
+ - [cx](#class-merger-cx) - Utility for composing class names conditionally.
66
44
 
67
- You can add variants by using the `variants` key. There's no limit to how many variants you can add.
45
+ ### Class Variants ([cv](./src/cv.ts))
46
+ For managing class names for a single component:
68
47
 
69
48
  ```ts
70
49
  import { cv } from 'css-variants'
71
-
50
+
72
51
  const button = cv({
73
- base: 'font-bold rounded-sm',
52
+ base: 'font-bold rounded-lg',
74
53
  variants: {
75
54
  color: {
76
- primary: 'bg-blue-500 hover:bg-blue-700',
77
- secondary: 'bg-purple-500 hover:bg-purple-700',
78
- success: 'hover:bg-green-700',
55
+ primary: 'bg-blue-500 text-white',
56
+ secondary: 'bg-gray-500 text-white'
79
57
  },
58
+ size: {
59
+ sm: 'text-sm px-2 py-1',
60
+ lg: 'text-lg px-4 py-2'
61
+ }
80
62
  },
63
+ compoundVariants: [
64
+ {
65
+ color: 'primary',
66
+ size: 'lg',
67
+ className: 'uppercase'
68
+ }
69
+ ],
70
+ defaultVariants: {
71
+ color: 'primary',
72
+ size: 'sm'
73
+ }
81
74
  })
82
75
 
83
- button({ color: 'secondary' })
84
- // => 'font-bold rounded-sm bg-purple-500 hover:bg-purple-700'
76
+ // Usage
77
+ button() // => 'font-bold rounded-lg bg-blue-500 text-white text-sm px-2 py-1'
78
+
79
+ button({ size: 'lg' }) // => 'font-bold rounded-lg bg-blue-500 text-white text-lg px-4 py-2 uppercase'
80
+
81
+ button({ size: 'lg', className: 'custom' }) // => 'font-bold rounded-lg bg-blue-500 text-white text-lg px-4 py-2 uppercase custom'
85
82
  ```
86
83
 
84
+ ### Style Variants ([sv](./src/sv.ts))
85
+
86
+ For managing inline styles:
87
+
87
88
  ```ts
88
89
  import { sv } from 'css-variants'
89
-
90
+
90
91
  const button = sv({
91
92
  base: {
92
- fontWeight: 700,
93
+ fontWeight: 'bold',
94
+ borderRadius: '8px'
93
95
  },
94
96
  variants: {
95
97
  color: {
96
98
  primary: {
97
- color: 'blue',
99
+ backgroundColor: 'blue',
100
+ color: 'white'
98
101
  },
99
102
  secondary: {
100
- color: 'red',
101
- },
102
- success: {
103
- color: 'green',
104
- },
105
- },
106
- },
103
+ backgroundColor: 'gray',
104
+ color: 'white'
105
+ }
106
+ }
107
+ }
107
108
  })
108
109
 
109
- button({ color: 'secondary' })
110
- // => { fontWeight: 700, color: 'red' }
110
+ // Usage
111
+ button({ color: 'primary' })
112
+ // => { fontWeight: 'bold', borderRadius: '8px', backgroundColor: 'blue', color: 'white' }
113
+
114
+ button({
115
+ color: 'secondary',
116
+ style: { padding: '4px' },
117
+ })
118
+ // => { fontWeight: 'bold', borderRadius: '8px', backgroundColor: 'gray', color: 'white', padding: '4px' }
111
119
  ```
112
120
 
113
- ### Multiple variants
121
+ ### Slot Class Variants ([scv](./src/scv.ts))
114
122
 
115
- You can add multiple variants to a single component.
123
+ For managing class names across multiple slots/elements:
116
124
 
117
125
  ```ts
118
- import { cv } from 'css-variants'
126
+ import { scv } from 'css-variants'
119
127
 
120
- const button = cv({
121
- base: 'font-bold',
128
+ const card = scv({
129
+ slots: ['root', 'title', 'content'],
130
+ base: {
131
+ root: 'rounded-lg shadow',
132
+ title: 'text-xl font-bold',
133
+ content: 'mt-2'
134
+ },
122
135
  variants: {
123
- color: {
124
- primary: 'bg-blue-500 hover:bg-blue-700',
125
- secondary: 'bg-purple-500 hover:bg-purple-700',
126
- success: 'bg-green-500 hover:bg-green-700'
127
- },
128
136
  size: {
129
- sm: 'text-sm p-2',
130
- md: 'text-md p-4',
131
- lg: 'text-lg p-6',
132
- },
133
- },
137
+ sm: {
138
+ root: 'p-4',
139
+ title: 'text-base'
140
+ },
141
+ lg: {
142
+ root: 'p-6',
143
+ title: 'text-2xl'
144
+ }
145
+ }
146
+ }
134
147
  })
135
148
 
136
- button({ color: 'success', size: 'lg' })
137
- // => 'font-bold bg-green-500 hover:bg-green-700 text-lg p-6'
149
+ // Usage
150
+ card({ size: 'sm' })
151
+ // => {
152
+ // root: 'rounded-lg shadow p-4',
153
+ // title: 'text-xl font-bold text-base',
154
+ // content: 'mt-2'
155
+ // }
156
+
157
+ card({
158
+ size: 'lg',
159
+ classNames: {
160
+ content: 'custom',
161
+ },
162
+ })
163
+ // => {
164
+ // root: 'rounded-lg shadow p-6',
165
+ // title: 'text-xl font-bold text-2xl',
166
+ // content: 'mt-2 custom'
167
+ // }
138
168
  ```
139
169
 
170
+ ### Slot Style Variants ([ssv](./src/ssv.ts))
171
+
172
+ For managing inline styles across multiple slots:
173
+
140
174
  ```ts
141
- import { sv } from 'css-variants'
175
+ import { ssv } from 'css-variants'
142
176
 
143
- const button = sv({
177
+ const card = ssv({
178
+ slots: ['root', 'title'],
144
179
  base: {
145
- fontWeight: 700,
180
+ root: { padding: '1rem' },
181
+ title: { fontWeight: 'bold' }
146
182
  },
147
183
  variants: {
148
184
  size: {
149
- small: { fontSize: '12px' },
150
- large: { fontSize: '24px' },
151
- },
152
- color: {
153
- red: { color: 'red' },
154
- blue: { color: 'blue' },
155
- },
156
- },
185
+ sm: {
186
+ root: { maxWidth: '300px' },
187
+ title: { fontSize: '14px' }
188
+ },
189
+ lg: {
190
+ root: { maxWidth: '600px' },
191
+ title: { fontSize: '18px' }
192
+ }
193
+ }
194
+ }
157
195
  })
158
196
 
159
- button({ size: 'small', color: 'blue' })
160
- // => { fontWeight: 700, fontSize: '12px', color: 'blue' }
161
- ```
162
-
163
- ### Boolean variants
164
-
165
- You can also add boolean variants to a component. This is useful when you want to add a state variant e.g. `disabled`.
197
+ // Usage
198
+ card({ size: 'sm' })
199
+ // => {
200
+ // root: { padding: '1rem', maxWidth: '300px' },
201
+ // title: { fontWeight: 'bold', fontSize: '14px' }
202
+ // }
166
203
 
167
- ```ts
168
- import { cv } from 'css-variants'
169
-
170
- const button = cv({
171
- variants: {
172
- color: {
173
- primary: 'bg-blue-500 hover:bg-blue-700',
174
- secondary: 'bg-purple-500 hover:bg-purple-700',
175
- success: 'bg-green-500 hover:bg-green-700'
176
- },
177
- disabled: {
178
- true: 'opacity-50 pointer-events-none',
204
+ card({
205
+ size: 'lg',
206
+ styles: {
207
+ title: {
208
+ color: 'red',
179
209
  },
180
210
  },
181
211
  })
212
+ // => {
213
+ // root: { padding: '1rem', maxWidth: '600px' },
214
+ // title: { fontWeight: 'bold', fontSize: '18px', color: 'red' }
215
+ // }
216
+ ```
217
+
218
+ ### Class Merger ([cx](./src/cx.ts))
182
219
 
183
- button({ disabled: true })
184
- // => 'opacity-50 pointer-events-none'
220
+ Similar to `clsx/classnames` but with better TypeScript support.
221
+
222
+ ```tsx
223
+ import { cx } from 'css-variants'
224
+
225
+ // Basic usage
226
+ cx('foo', 'bar') // => 'foo bar'
227
+
228
+ // With conditions
229
+ cx('foo', {
230
+ 'bar': true,
231
+ 'baz': false
232
+ }) // => 'foo bar'
233
+
234
+ // With arrays
235
+ cx('foo', ['bar', 'baz']) // => 'foo bar baz'
236
+
237
+ // With nested structures
238
+ cx('foo', {
239
+ bar: true,
240
+ baz: [
241
+ 'qux',
242
+ { quux: true }
243
+ ]
244
+ }) // => 'foo bar qux quux'
245
+
246
+ // With falsy values (they're ignored)
247
+ cx('foo', null, undefined, false, 0, '') // => 'foo'
185
248
  ```
186
249
 
187
- ### Compound variants
250
+ ## Advanced Features
251
+
252
+ ### Boolean Variants
188
253
 
189
- Sometimes you might want to add a variant that depends on another variant. This is possible by using the `compoundVariants` key.
254
+ Support for boolean variants to toggle styles conditionally:
190
255
 
191
256
  ```ts
192
257
  import { cv } from 'css-variants'
193
-
258
+
194
259
  const button = cv({
195
260
  variants: {
196
- size: {
197
- sm: 'text-sm p-2',
198
- md: 'text-md p-4',
199
- lg: 'text-lg',
200
- },
201
261
  disabled: {
202
- true: 'opacity-50 pointer-events-none',
203
- },
204
- },
205
- compoundVariants: [
206
- {
207
- size: 'lg', // You can also use the values as an array
208
- disabled: true,
209
- className: 'uppercase',
262
+ true: 'opacity-50 cursor-not-allowed'
210
263
  }
211
- ],
264
+ }
212
265
  })
213
266
 
214
- button({ size: 'lg', disabled: true })
215
- // => 'text-lg p-6 opacity-50 pointer-events-none uppercase'
267
+ button({ disabled: true }) // => 'opacity-50 cursor-not-allowed'
216
268
  ```
217
269
 
218
- ### Default variants
270
+ ### Compound Variants
219
271
 
220
- You can also add a default variant to a component. This is useful when you want to add a predefined variants values to a component.
272
+ Apply styles based on combinations of variants:
221
273
 
222
274
  ```ts
223
275
  import { cv } from 'css-variants'
224
-
276
+
225
277
  const button = cv({
226
- base: 'font-bold rounded-sm',
227
278
  variants: {
228
279
  color: {
229
- primary: 'bg-blue-500 hover:bg-blue-700',
230
- secondary: 'bg-purple-500 hover:bg-purple-700',
231
- success: 'bg-green-500 hover:bg-green-700'
280
+ primary: 'bg-blue-500',
281
+ danger: 'bg-red-500'
232
282
  },
283
+ size: {
284
+ sm: 'text-sm',
285
+ lg: 'text-lg'
286
+ }
233
287
  },
234
- defaultVariants: {
235
- color: 'primary',
236
- },
288
+ compoundVariants: [
289
+ {
290
+ color: 'danger',
291
+ size: 'lg',
292
+ className: 'animate-pulse'
293
+ }
294
+ ]
237
295
  })
238
296
 
239
- button()
240
- // => 'font-bold rounded-sm bg-blue-500 hover:bg-blue-700'
297
+ button({ color: 'danger', size: 'lg' }) // => 'bg-red-500 text-lg animate-pulse'
241
298
  ```
242
299
 
243
- ## Slots
244
-
245
- Slots allows you to separate a component into multiple parts.
246
-
247
- ### Basic Usage
248
-
249
- You can add `slots` by using the slots key. There's no limit to how many slots you can add.
250
-
251
- ```ts
252
- import { scv } from 'css-variants'
253
-
254
- const notification = scv({
255
- slots: ['root', 'title'],
256
- base: {
257
- root: 'root',
258
- title: 'title',
259
- },
260
- })
300
+ ### Default Variants
261
301
 
262
- notification()
263
- /**
264
- * Result:
265
- * {
266
- * root: 'root',
267
- * title: 'title',
268
- * }
269
- */
270
- ```
302
+ Specify default variant values:
271
303
 
272
304
  ```ts
273
- import { ssv } from 'css-variants'
305
+ import { cv } from 'css-variants'
274
306
 
275
- const notification = ssv({
276
- slots: ['root', 'title'],
277
- base: {
278
- root: {
279
- fontWeight: 'bold',
280
- },
281
- title: {
282
- fontSize: '20px',
307
+ const button = cv({
308
+ variants: {
309
+ size: {
310
+ sm: 'text-sm',
311
+ lg: 'text-lg'
283
312
  }
284
313
  },
314
+ defaultVariants: {
315
+ size: 'sm'
316
+ }
285
317
  })
286
318
 
287
- notification()
288
- /**
289
- * Result:
290
- * {
291
- * root: { fontWeight: 'bold' },
292
- * title: { fontSize: '20px' },
293
- * }
294
- */
319
+ button() // => 'text-sm'
295
320
  ```
296
321
 
297
- ### Slots with variants
322
+ ### Custom Class Name Resolver
298
323
 
299
- You can also change the entire component and its slots by using the variants.
324
+ Use your preferred class name utility:
300
325
 
301
326
  ```ts
302
- import { csv } from 'css-variants'
327
+ import { cv } from 'css-variants'
328
+ import { clsx } from 'clsx'
303
329
 
304
- const notification = cv({
305
- slots: ['root', 'title', 'content'],
306
- base: {
307
- root: 'root',
308
- title: 'title',
309
- content: 'content',
310
- },
330
+ const button = cv({
331
+ base: 'btn',
311
332
  variants: {
312
333
  color: {
313
- primary: {
314
- root: 'root-primary',
315
- title: 'title-primary',
316
- content: 'content-primary',
317
- },
318
- secondary: {
319
- title: 'title-secondary',
320
- content: 'content-secondary',
321
- },
334
+ primary: 'btn-primary'
322
335
  }
323
336
  },
337
+ classNameResolver: clsx
324
338
  })
325
-
326
- notification({ color: 'primary' })
327
- /**
328
- * Result:
329
- * {
330
- * root: 'root root-primary',
331
- * title: 'title title-primary',
332
- * content: 'content content-primary',
333
- * }
334
- */
335
-
336
- notification({ color: 'secondary' })
337
- /**
338
- * Result:
339
- * {
340
- * root: 'root',
341
- * title: 'title title-secondary',
342
- * content: 'content content-secondary',
343
- * }
344
- */
345
339
  ```
346
340
 
347
- ## Overriding styles
348
-
349
- `css-variants` allows you to override or extend the styles of your components. This feature is useful when you need to add custom styles or modify existing ones without changing the original component definition.
341
+ ## TypeScript Support
350
342
 
351
- ### Overriding styles on a single component
352
-
353
- You can override or extend styles for a single component by passing additional `className` and `style` properties when calling the component function. These will be merged with the existing styles.
343
+ Full TypeScript support with automatic type inference:
354
344
 
355
345
  ```ts
356
346
  import { cv } from 'css-variants'
357
-
358
- const button = cv({
359
- base: 'font-semibold',
360
- variants: {
361
- color: {
362
- primary: 'bg-blue-500 hover:bg-blue-700',
363
- secondary: 'bg-purple-500 hover:bg-purple-700',
364
- }
365
- }
366
- })
367
-
368
- button({
369
- color: 'secondary',
370
- className: 'border-purple-600',
371
- })
372
- // => 'bg-purple-500 hover:bg-purple-700 border-purple-600'
373
- ```
374
347
 
375
- ```ts
376
- import { sv } from 'css-variants'
377
-
378
348
  const button = cv({
379
- base: {
380
- fontWeight: 700,
381
- },
382
349
  variants: {
383
- color: {
384
- primary: {
385
- color: 'blue',
386
- },
387
- secondary: {
388
- color: 'purple',
389
- },
350
+ size: {
351
+ sm: 'text-sm',
352
+ lg: 'text-lg'
390
353
  }
391
354
  }
392
355
  })
393
-
394
- button({
395
- color: 'secondary',
396
- style: { color: 'red' },
397
- })
398
- // => { fontWeight: 700, color: 'red' }
399
- ```
400
-
401
- ### Overriding styles on a component with slots
402
-
403
- For components with slots, you can override styles using the `classNames` and `styles` properties. These allow you to target specific slots and apply custom classes or inline styles.
404
-
405
- ```ts
406
- import { csv } from 'css-variants'
407
-
408
- const notification = cv({
409
- slots: ['root', 'title'],
410
- base: {
411
- root: 'root',
412
- title: 'title',
413
- },
414
- })
415
-
416
- notification({
417
- classNames: {
418
- root: 'root-custom-class',
419
- },
420
- })
421
- /**
422
- * Result:
423
- * {
424
- * root: 'root root-custom-class',
425
- * title: 'title',
426
- * }
427
- */
428
356
 
357
+ type ButtonProps = Parameters<typeof button>[0]
358
+ // => { size?: 'sm' | 'lg' | undefined }
429
359
  ```
430
360
 
431
- ## Custom function to resolve class names
432
-
433
- Default is the internal 'cx' function. The resolver function should accept multiple class name arguments and return a single concatenated string.
434
-
435
- You can use this to integrate with class name utilities like clsx, classnames, or your own custom class name resolution logic.
436
-
437
- ```ts
438
- import { clsx } from 'clsx'
439
- import { cv as _cv, scv as _scv } from 'css-variants'
440
-
441
- export const cv: typeof _cv = (config) => {
442
- return _cv({
443
- ...config,
444
- classNameResolver: clsx,
445
- })
446
- }
447
-
448
- export const scv: typeof _scv = (config) => {
449
- return _scv({
450
- ...config,
451
- classNameResolver: clsx,
452
- })
453
- }
454
- ```
361
+ ## Inspiration
455
362
 
456
- ## TypeScript
363
+ This library is inspired by several excellent projects:
457
364
 
458
- ### Extracting Variant Types
459
-
460
- ```tsx
461
- import { cv } from 'css-variants'
462
-
463
- export const button = cv({
464
- variants: {
465
- color: {
466
- primary: 'bg-blue-500 text-white',
467
- secondary: 'bg-purple-500 text-white',
468
- },
469
- },
470
- })
471
-
472
- export type ButtonProps = Parameters<typeof button>[0]
473
- ```
365
+ - [CVA (Class Variance Authority)](https://github.com/joe-bell/cva)
366
+ - [Panda CSS](https://github.com/chakra-ui/panda)
474
367
 
475
368
  ## Contribute
476
369
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "css-variants",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "A lightweight, flexible API for managing CSS class variants.",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",