slot-variants 1.0.0
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.
- package/LICENSE +21 -0
- package/README.md +662 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +81 -0
- package/dist/index.js +1 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nicu Micleușanu
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
# slot-variants
|
|
2
|
+
|
|
3
|
+
A lightweight, zero-dependency, type-safe library for managing class name variants with slots support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install slot-variants
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
`slot-variants` exports two functions:
|
|
14
|
+
|
|
15
|
+
- **`sv()`** - creates variant-based class name generators with optional slots
|
|
16
|
+
- **`cn()`** - a utility for conditionally merging class names
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { sv } from 'slot-variants';
|
|
22
|
+
|
|
23
|
+
const button = sv('btn font-medium rounded-lg', {
|
|
24
|
+
variants: {
|
|
25
|
+
size: {
|
|
26
|
+
sm: 'text-sm py-1 px-2',
|
|
27
|
+
md: 'text-base py-2 px-4',
|
|
28
|
+
lg: 'text-lg py-3 px-6'
|
|
29
|
+
},
|
|
30
|
+
intent: {
|
|
31
|
+
primary: 'bg-blue-500 text-white',
|
|
32
|
+
secondary: 'bg-gray-200 text-gray-800',
|
|
33
|
+
danger: 'bg-red-500 text-white'
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
defaultVariants: {
|
|
37
|
+
size: 'md',
|
|
38
|
+
intent: 'primary'
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
button();
|
|
43
|
+
// 'btn font-medium rounded-lg text-base py-2 px-4 bg-blue-500 text-white'
|
|
44
|
+
|
|
45
|
+
button({ size: 'lg', intent: 'danger' });
|
|
46
|
+
// 'btn font-medium rounded-lg text-lg py-3 px-6 bg-red-500 text-white'
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## `cn()` - Class Name Utility
|
|
50
|
+
|
|
51
|
+
A utility for conditionally joining class names together.
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { cn } from 'slot-variants';
|
|
55
|
+
|
|
56
|
+
// Strings
|
|
57
|
+
cn('foo', 'bar'); // 'foo bar'
|
|
58
|
+
|
|
59
|
+
// Arrays (including nested)
|
|
60
|
+
cn(['foo', 'bar']); // 'foo bar'
|
|
61
|
+
cn(['foo', ['bar', 'baz']]); // 'foo bar baz'
|
|
62
|
+
|
|
63
|
+
// Objects (truthy values are included)
|
|
64
|
+
cn({ foo: true, bar: false, baz: true }); // 'foo baz'
|
|
65
|
+
|
|
66
|
+
// Mixed
|
|
67
|
+
cn('base', ['responsive'], { active: true }); // 'base active responsive'
|
|
68
|
+
|
|
69
|
+
// Falsy values are filtered out
|
|
70
|
+
cn('foo', null, undefined, false, 'bar'); // 'foo bar'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Supported Input Types
|
|
74
|
+
|
|
75
|
+
| Type | Behavior |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `string` | Included as-is |
|
|
78
|
+
| `string[]` | Flattened recursively |
|
|
79
|
+
| `Record<string, unknown>` | Keys with truthy values included |
|
|
80
|
+
| `boolean`, `number`, `bigint` | Ignored |
|
|
81
|
+
| `null`, `undefined` | Ignored |
|
|
82
|
+
|
|
83
|
+
## `sv()` - Slot Variants
|
|
84
|
+
|
|
85
|
+
### Base Only (No Config)
|
|
86
|
+
|
|
87
|
+
When called without a config object, `sv()` processes the base classes through `cn()` and returns a string:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
sv('btn btn-primary'); // 'btn btn-primary'
|
|
91
|
+
sv(['btn', 'btn-primary']); // 'btn btn-primary'
|
|
92
|
+
sv({ btn: true, disabled: false }); // 'btn'
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Variants
|
|
96
|
+
|
|
97
|
+
When a config object is provided, `sv()` returns a function that accepts variant props and returns the computed class string.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const badge = sv('badge', {
|
|
101
|
+
variants: {
|
|
102
|
+
color: {
|
|
103
|
+
gray: 'bg-gray-100 text-gray-800',
|
|
104
|
+
red: 'bg-red-100 text-red-800',
|
|
105
|
+
green: 'bg-green-100 text-green-800'
|
|
106
|
+
},
|
|
107
|
+
size: {
|
|
108
|
+
sm: 'text-xs px-2 py-0.5',
|
|
109
|
+
lg: 'text-base px-3 py-1'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
badge({ color: 'green', size: 'sm' });
|
|
115
|
+
// 'badge bg-green-100 text-green-800 text-xs px-2 py-0.5'
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Variant values can be strings, arrays, or objects:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const button = sv('btn', {
|
|
122
|
+
variants: {
|
|
123
|
+
size: {
|
|
124
|
+
sm: ['px-2', 'py-1', 'text-sm'], // array
|
|
125
|
+
lg: 'px-6 py-3 text-lg', // string
|
|
126
|
+
xl: { 'px-8': true, 'py-4': true, 'text-xl': true } // object
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Boolean Variants
|
|
133
|
+
|
|
134
|
+
Variants with `true`/`false` keys accept boolean prop values:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const input = sv('input border', {
|
|
138
|
+
variants: {
|
|
139
|
+
disabled: {
|
|
140
|
+
true: 'opacity-50 cursor-not-allowed',
|
|
141
|
+
false: 'cursor-text'
|
|
142
|
+
},
|
|
143
|
+
error: {
|
|
144
|
+
true: 'border-red-500',
|
|
145
|
+
false: 'border-gray-300'
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
defaultVariants: {
|
|
149
|
+
disabled: false,
|
|
150
|
+
error: false
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
input({ disabled: true, error: true });
|
|
155
|
+
// 'input border opacity-50 cursor-not-allowed border-red-500'
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Boolean shorthand** - provide a `ClassValue` directly instead of a `true`/`false` record. The value is applied when `true`, and nothing is applied when `false`:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
const button = sv('btn', {
|
|
162
|
+
variants: {
|
|
163
|
+
loading: 'animate-spin pointer-events-none',
|
|
164
|
+
disabled: 'opacity-50 cursor-not-allowed'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
button({ loading: true, disabled: false });
|
|
169
|
+
// 'btn animate-spin pointer-events-none'
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Numeric Variant Keys
|
|
173
|
+
|
|
174
|
+
Variant keys can be numbers:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const heading = sv('font-bold', {
|
|
178
|
+
variants: {
|
|
179
|
+
level: {
|
|
180
|
+
1: 'text-4xl',
|
|
181
|
+
2: 'text-3xl',
|
|
182
|
+
3: 'text-2xl'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
heading({ level: 1 }); // 'font-bold text-4xl'
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Default Variants
|
|
191
|
+
|
|
192
|
+
Set fallback values that are used when a variant prop is not provided:
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
const button = sv('btn', {
|
|
196
|
+
variants: {
|
|
197
|
+
size: {
|
|
198
|
+
sm: 'text-sm',
|
|
199
|
+
md: 'text-base',
|
|
200
|
+
lg: 'text-lg'
|
|
201
|
+
},
|
|
202
|
+
rounded: {
|
|
203
|
+
true: 'rounded-full',
|
|
204
|
+
false: 'rounded-md'
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
defaultVariants: {
|
|
208
|
+
size: 'md',
|
|
209
|
+
rounded: false
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
button(); // 'btn text-base rounded-md'
|
|
214
|
+
button({ size: 'lg' }); // 'btn text-lg rounded-md'
|
|
215
|
+
button({ rounded: true }); // 'btn text-base rounded-full'
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Passing `undefined` for a prop falls back to the default:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
button({ size: undefined }); // 'btn text-base rounded-md'
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Function-Based Default Variants
|
|
225
|
+
|
|
226
|
+
Default variants can be functions that receive the current props and return a value dynamically. Return `undefined` to skip the variant entirely:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
const button = sv('btn', {
|
|
230
|
+
variants: {
|
|
231
|
+
size: {
|
|
232
|
+
sm: 'text-sm',
|
|
233
|
+
lg: 'text-lg'
|
|
234
|
+
},
|
|
235
|
+
intent: {
|
|
236
|
+
primary: 'bg-blue-500',
|
|
237
|
+
danger: 'bg-red-500'
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
defaultVariants: {
|
|
241
|
+
size: 'sm',
|
|
242
|
+
intent: (props) => (props.size === 'lg' ? 'danger' : 'primary')
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
button(); // 'btn text-sm bg-blue-500'
|
|
247
|
+
button({ size: 'lg' }); // 'btn text-lg bg-red-500'
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Compound Variants
|
|
251
|
+
|
|
252
|
+
Apply additional classes when multiple variant conditions are met simultaneously:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
const button = sv('btn', {
|
|
256
|
+
variants: {
|
|
257
|
+
intent: {
|
|
258
|
+
primary: 'bg-blue-500',
|
|
259
|
+
secondary: 'bg-gray-200'
|
|
260
|
+
},
|
|
261
|
+
size: {
|
|
262
|
+
sm: 'text-sm',
|
|
263
|
+
lg: 'text-lg'
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
compoundVariants: [
|
|
267
|
+
{
|
|
268
|
+
intent: 'primary',
|
|
269
|
+
size: 'lg',
|
|
270
|
+
class: 'uppercase font-bold'
|
|
271
|
+
}
|
|
272
|
+
]
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
button({ intent: 'primary', size: 'lg' });
|
|
276
|
+
// 'btn bg-blue-500 text-lg uppercase font-bold'
|
|
277
|
+
|
|
278
|
+
button({ intent: 'secondary', size: 'lg' });
|
|
279
|
+
// 'btn bg-gray-200 text-lg'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Compound variant conditions support **array matching** (OR logic):
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
compoundVariants: [
|
|
286
|
+
{
|
|
287
|
+
intent: ['primary', 'secondary'],
|
|
288
|
+
size: 'sm',
|
|
289
|
+
class: 'tracking-tight'
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Multiple compound variants can match simultaneously, and all matching classes are applied.
|
|
295
|
+
|
|
296
|
+
Compound variants also support `className` as an alternative to `class`:
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
compoundVariants: [
|
|
300
|
+
{
|
|
301
|
+
size: 'sm',
|
|
302
|
+
className: 'shadow-sm'
|
|
303
|
+
}
|
|
304
|
+
]
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
### Required Variants
|
|
308
|
+
|
|
309
|
+
Mark variants as required so they must be provided at call time. Required variants cannot have default values:
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
const button = sv('btn', {
|
|
313
|
+
variants: {
|
|
314
|
+
size: {
|
|
315
|
+
sm: 'text-sm',
|
|
316
|
+
lg: 'text-lg'
|
|
317
|
+
},
|
|
318
|
+
intent: {
|
|
319
|
+
primary: 'bg-blue-500',
|
|
320
|
+
danger: 'bg-red-500'
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
requiredVariants: ['intent']
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
button({ intent: 'primary' }); // OK
|
|
327
|
+
button({ intent: 'primary', size: 'lg' }); // OK
|
|
328
|
+
button({ size: 'lg' }); // Throws: Missing required variant: "intent"
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Slots
|
|
332
|
+
|
|
333
|
+
Slots allow you to define multiple named class targets for multi-element components. When slots are defined, the returned function produces an object with `base` and each named slot as keys:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const card = sv('card border rounded-lg', {
|
|
337
|
+
slots: {
|
|
338
|
+
header: 'card-header font-semibold',
|
|
339
|
+
body: 'card-body',
|
|
340
|
+
footer: 'card-footer border-t'
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
const { base, header, body, footer } = card();
|
|
345
|
+
// base: 'card border rounded-lg'
|
|
346
|
+
// header: 'card-header font-semibold'
|
|
347
|
+
// body: 'card-body'
|
|
348
|
+
// footer: 'card-footer border-t'
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
The `base` slot can also be defined explicitly in the slots config, and it merges with the first argument:
|
|
352
|
+
|
|
353
|
+
```typescript
|
|
354
|
+
const card = sv('border', {
|
|
355
|
+
slots: {
|
|
356
|
+
base: 'rounded-lg shadow-md',
|
|
357
|
+
header: 'font-bold'
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
card().base; // 'border rounded-lg shadow-md'
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
#### Slots with Variants
|
|
365
|
+
|
|
366
|
+
Variant values can target specific slots by providing an object with slot keys:
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const card = sv('card border rounded-lg', {
|
|
370
|
+
slots: {
|
|
371
|
+
header: 'font-bold',
|
|
372
|
+
body: 'py-4',
|
|
373
|
+
footer: 'border-t'
|
|
374
|
+
},
|
|
375
|
+
variants: {
|
|
376
|
+
size: {
|
|
377
|
+
sm: {
|
|
378
|
+
base: 'p-2 text-sm',
|
|
379
|
+
header: 'pb-1',
|
|
380
|
+
body: 'py-1',
|
|
381
|
+
footer: 'pt-1'
|
|
382
|
+
},
|
|
383
|
+
lg: {
|
|
384
|
+
base: 'p-6 text-lg',
|
|
385
|
+
header: 'pb-4',
|
|
386
|
+
body: 'py-4',
|
|
387
|
+
footer: 'pt-4'
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
defaultVariants: {
|
|
392
|
+
size: 'sm'
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
const { base, header, body, footer } = card({ size: 'lg' });
|
|
397
|
+
// base: 'card border rounded-lg p-6 text-lg'
|
|
398
|
+
// header: 'font-bold pb-4'
|
|
399
|
+
// body: 'py-4 py-4'
|
|
400
|
+
// footer: 'border-t pt-4'
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
Variants don't need to target every slot - untargeted slots remain unchanged:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
variants: {
|
|
407
|
+
size: {
|
|
408
|
+
sm: { base: 'p-2', header: 'text-sm' }
|
|
409
|
+
// body and footer are unaffected
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
#### Boolean Shorthand with Slots
|
|
415
|
+
|
|
416
|
+
When using slots, a boolean shorthand variant can be a slot object:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
const card = sv('border rounded-lg', {
|
|
420
|
+
slots: {
|
|
421
|
+
header: 'font-bold',
|
|
422
|
+
body: 'py-4'
|
|
423
|
+
},
|
|
424
|
+
variants: {
|
|
425
|
+
highlighted: {
|
|
426
|
+
base: 'ring-2 ring-blue-500',
|
|
427
|
+
header: 'bg-blue-100'
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
card({ highlighted: true });
|
|
433
|
+
// base: 'border rounded-lg ring-2 ring-blue-500'
|
|
434
|
+
// header: 'font-bold bg-blue-100'
|
|
435
|
+
|
|
436
|
+
card({ highlighted: false });
|
|
437
|
+
// base: 'border rounded-lg'
|
|
438
|
+
// header: 'font-bold'
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### Compound Slots
|
|
442
|
+
|
|
443
|
+
Apply classes to multiple slots at once, optionally conditioned on variant values:
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
const dialog = sv('fixed inset-0', {
|
|
447
|
+
slots: {
|
|
448
|
+
overlay: 'bg-black/50',
|
|
449
|
+
content: 'bg-white rounded-lg',
|
|
450
|
+
title: 'text-lg font-bold',
|
|
451
|
+
actions: 'flex gap-2'
|
|
452
|
+
},
|
|
453
|
+
variants: {
|
|
454
|
+
size: {
|
|
455
|
+
sm: 'max-w-sm',
|
|
456
|
+
lg: 'max-w-lg'
|
|
457
|
+
}
|
|
458
|
+
},
|
|
459
|
+
compoundSlots: [
|
|
460
|
+
{
|
|
461
|
+
slots: ['content', 'title', 'actions'],
|
|
462
|
+
class: 'px-6'
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
size: 'sm',
|
|
466
|
+
slots: ['title', 'actions'],
|
|
467
|
+
class: 'text-sm'
|
|
468
|
+
}
|
|
469
|
+
]
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
const result = dialog({ size: 'sm' });
|
|
473
|
+
// base: 'fixed inset-0 max-w-sm'
|
|
474
|
+
// overlay: 'bg-black/50'
|
|
475
|
+
// content: 'bg-white rounded-lg px-6'
|
|
476
|
+
// title: 'text-lg font-bold px-6 text-sm'
|
|
477
|
+
// actions: 'flex gap-2 px-6 text-sm'
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Compound slots support the same array matching as compound variants:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
compoundSlots: [
|
|
484
|
+
{
|
|
485
|
+
size: ['sm', 'md'],
|
|
486
|
+
slots: ['cell', 'header'],
|
|
487
|
+
class: 'px-3'
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Class Override at Runtime
|
|
493
|
+
|
|
494
|
+
Append additional classes at call time using `class` or `className`:
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
const button = sv('btn', {
|
|
498
|
+
variants: {
|
|
499
|
+
size: {
|
|
500
|
+
sm: 'text-sm',
|
|
501
|
+
lg: 'text-lg'
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// String
|
|
507
|
+
button({ size: 'sm', class: 'mt-4 mx-auto' });
|
|
508
|
+
// 'btn text-sm mt-4 mx-auto'
|
|
509
|
+
|
|
510
|
+
// Array
|
|
511
|
+
button({ size: 'sm', class: ['mt-4', 'mx-auto'] });
|
|
512
|
+
// 'btn text-sm mt-4 mx-auto'
|
|
513
|
+
|
|
514
|
+
// Object
|
|
515
|
+
button({ size: 'sm', class: { 'mt-4': true, hidden: false } });
|
|
516
|
+
// 'btn text-sm mt-4'
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
With slots, a string `class` appends to the base slot. Use a slot object to target specific slots:
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
const card = sv('border', {
|
|
523
|
+
slots: {
|
|
524
|
+
header: 'font-bold',
|
|
525
|
+
body: 'py-4'
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
// String targets the base slot
|
|
530
|
+
card({ class: 'shadow-xl' });
|
|
531
|
+
// base: 'border shadow-xl', header: 'font-bold', body: 'py-4'
|
|
532
|
+
|
|
533
|
+
// Object targets specific slots
|
|
534
|
+
card({ class: { base: 'shadow-xl', header: 'text-blue-700', body: 'min-h-24' } });
|
|
535
|
+
// base: 'border shadow-xl', header: 'font-bold text-blue-700', body: 'py-4 min-h-24'
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Both `class` and `className` are supported, but `class` is prioritized when both are used in the same time.
|
|
539
|
+
|
|
540
|
+
### Post-Processing
|
|
541
|
+
|
|
542
|
+
Apply a custom transformation to the final class strings using `postProcess`. This is useful for integrating with libraries like `tailwind-merge`:
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
import { sv } from 'slot-variants';
|
|
546
|
+
import { twMerge } from 'tailwind-merge';
|
|
547
|
+
|
|
548
|
+
const button = sv('px-4 py-2 bg-blue-500', {
|
|
549
|
+
variants: {
|
|
550
|
+
size: {
|
|
551
|
+
sm: 'px-2 py-1 text-sm',
|
|
552
|
+
lg: 'px-6 py-3 text-lg'
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
postProcess: twMerge
|
|
556
|
+
});
|
|
557
|
+
```
|
|
558
|
+
|
|
559
|
+
The `postProcess` function is applied to each slot's final class string independently.
|
|
560
|
+
|
|
561
|
+
### Caching
|
|
562
|
+
|
|
563
|
+
Results are cached automatically for performance. The default cache size is **256** entries. Calls with `class` or `className` props bypass the cache.
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
const button = sv('btn', {
|
|
567
|
+
variants: {
|
|
568
|
+
size: {
|
|
569
|
+
sm: 'text-sm',
|
|
570
|
+
lg: 'text-lg'
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
cacheSize: 512 // customize the cache size
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
button.getCacheSize(); // current number of cached entries
|
|
577
|
+
button.clearCache(); // clear all cached entries
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
### Introspection
|
|
581
|
+
|
|
582
|
+
The returned function exposes configuration properties for runtime introspection:
|
|
583
|
+
|
|
584
|
+
```typescript
|
|
585
|
+
const button = sv('btn', {
|
|
586
|
+
slots: {
|
|
587
|
+
icon: 'w-4 h-4'
|
|
588
|
+
},
|
|
589
|
+
variants: {
|
|
590
|
+
size: {
|
|
591
|
+
sm: 'text-sm',
|
|
592
|
+
lg: 'text-lg'
|
|
593
|
+
},
|
|
594
|
+
intent: {
|
|
595
|
+
primary: 'bg-blue-500',
|
|
596
|
+
danger: 'bg-red-500'
|
|
597
|
+
}
|
|
598
|
+
},
|
|
599
|
+
defaultVariants: {
|
|
600
|
+
size: 'sm'
|
|
601
|
+
},
|
|
602
|
+
requiredVariants: ['intent']
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
button.variantKeys; // ['size', 'intent']
|
|
606
|
+
button.variants; // { size: { sm: 'text-sm', lg: 'text-lg' }, intent: { ... } }
|
|
607
|
+
button.slotKeys; // ['base', 'icon']
|
|
608
|
+
button.slots; // { icon: 'w-4 h-4' }
|
|
609
|
+
button.defaultVariants; // { size: 'sm' }
|
|
610
|
+
button.requiredVariants; // ['intent']
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
## TypeScript
|
|
614
|
+
|
|
615
|
+
`slot-variants` is fully typed. Variant props are inferred from your config:
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
import { sv, type VariantProps } from 'slot-variants';
|
|
619
|
+
|
|
620
|
+
const button = sv('btn', {
|
|
621
|
+
variants: {
|
|
622
|
+
size: {
|
|
623
|
+
sm: 'text-sm',
|
|
624
|
+
lg: 'text-lg'
|
|
625
|
+
},
|
|
626
|
+
intent: {
|
|
627
|
+
primary: 'bg-blue-500',
|
|
628
|
+
danger: 'bg-red-500'
|
|
629
|
+
}
|
|
630
|
+
},
|
|
631
|
+
requiredVariants: ['intent']
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// Extract the variant props type (excludes class/className)
|
|
635
|
+
type ButtonProps = VariantProps<typeof button>;
|
|
636
|
+
// { size?: 'sm' | 'lg' | undefined; intent: 'primary' | 'danger' }
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
### Exported Types
|
|
640
|
+
|
|
641
|
+
| Type | Description |
|
|
642
|
+
| --- | --- |
|
|
643
|
+
| `ClassValue` | Valid input types for `cn()` |
|
|
644
|
+
| `VariantProps<T>` | Extracts variant props from an `sv()` return type |
|
|
645
|
+
|
|
646
|
+
### Return Type
|
|
647
|
+
|
|
648
|
+
- **Without slots** - the function returns a `string`
|
|
649
|
+
- **With slots** - the function returns a `Record` with `base` and each slot name as keys, all typed as `string`
|
|
650
|
+
|
|
651
|
+
## Config Reference
|
|
652
|
+
|
|
653
|
+
| Option | Type | Description |
|
|
654
|
+
| --- | --- | --- |
|
|
655
|
+
| `variants` | `SVVariants` | Variant definitions mapping variant names to their possible values |
|
|
656
|
+
| `slots` | `SVSlots` | Named slot definitions for multi-element components |
|
|
657
|
+
| `compoundVariants` | `Array` | Additional classes applied when multiple variant conditions match |
|
|
658
|
+
| `compoundSlots` | `Array` | Classes applied to multiple slots based on variant conditions |
|
|
659
|
+
| `defaultVariants` | `Object` | Default values for variants (static values or functions) |
|
|
660
|
+
| `requiredVariants` | `string[]` | Variant names that must be provided at call time |
|
|
661
|
+
| `postProcess` | `(className: string) => string` | Custom transformation applied to final class strings |
|
|
662
|
+
| `cacheSize` | `number` | Maximum number of cached results (default: `256`) |
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var T=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var G=(o,t)=>{for(var r in t)T(o,r,{get:t[r],enumerable:!0})},H=(o,t,r,V)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of M(t))!U.call(o,a)&&a!==r&&T(o,a,{get:()=>t[a],enumerable:!(V=I(t,a))||V.enumerable});return o};var J=o=>H(T({},"__esModule",{value:!0}),o);var X={};G(X,{cn:()=>x,sv:()=>j});module.exports=J(X);var{isArray:L}=Array,{keys:Q}=Object;function x(...o){let t=[...o],r=0,V="";for(;r<t.length;){let a=t[r];if(r++,!!a){if(typeof a=="string")V+=` ${a}`;else if(L(a))t.push(...a);else if(typeof a=="object")for(let y of Q(a))a[y]&&(V+=` ${y}`)}}return V.slice(1)}var{isArray:v}=Array,{assign:W,entries:R,keys:c}=Object,E=(o,t)=>o===t||`${o}`==`${t}`,z=(o,t)=>{for(let r of c(t)){if(r==="class"||r==="className"||r==="slots")continue;let V=t[r],a=o[r];if(v(V)){if(!V.some(y=>E(y,a)))return!1}else if(!E(V,a))return!1}return!0};function j(o,t){if(!t)return x(o);let{variants:r={},slots:V={},compoundVariants:a=[],compoundSlots:y=[],defaultVariants:C={},requiredVariants:K=[],cacheSize:N=256,postProcess:O}=t,f=new Map,h=(n,s)=>{if(n){if(f.size>=N){let i=f.keys().next().value;i&&f.delete(i)}f.set(n,s)}return s},{base:q,...w}=V,A=x(o,q),p=new Set(c(w)),g={};for(let[n,s]of R(r))if(s&&typeof s=="object"&&!v(s)){let i=c(s);p.size>0&&i.length>0&&i.every(S=>S==="base"||p.has(S))?g[n]={false:"",true:s}:i.every(S=>S==="true"||S==="false")?g[n]={true:"",false:"",...s}:g[n]=s}else g[n]={false:"",true:s};let D=R(g),k=new Set(c(g)),$=new Set(c(C)),F=n=>O?.(n)??n,B=n=>{if(!n||typeof n!="object"||v(n))return!1;for(let s of c(n))if(!(s==="base"||p.has(s)))return!1;return!0},b=(n,s)=>{if(B(s))for(let[i,u]of R(s))n[i]?.push(u);else n.base.push(s)};for(let n of K){if(!k.has(n))throw new Error(`Required variant "${n}" is not defined in variants`);if($.has(n))throw new Error(`Required variant "${n}" cannot have a default value`)}return W((n={})=>{let s=n.class??n.className,i={},u="";for(let e of k){let l=n[e];if(l===void 0){if($.has(e)){let d=C[e];typeof d=="function"?i[e]=d(n):i[e]=d,s||(u+=`${i[e]};`)}}else i[e]=l,s||(u+=`${l};`)}if(!s){let e=f.get(u);if(e)return e}for(let e of K)if(i[e]===void 0)throw new Error(`Missing required variant: "${e}"`);let S={base:[A]};for(let e of p)S[e]=[w[e]];for(let[e,l]of D){let d=i[e];if(d===void 0)continue;let P=l[String(d)];if(P===void 0)throw new Error(`Invalid value "${d}" for variant "${e}"`);P!==""&&b(S,P)}for(let e of a)z(i,e)&&b(S,e.class??e.className);for(let e of y)if(z(i,e)){let l=e.class??e.className;for(let d of e.slots)S[d]?.push(l)}s&&b(S,s);let m={base:""};for(let[e,l]of R(S))m[e]=F(x(l));return p.size===0?h(u,m.base):h(u,m)},{variants:r,variantKeys:c(r),slots:V,slotKeys:["base",...p],defaultVariants:C,requiredVariants:K,clearCache:()=>f.clear(),getCacheSize:()=>f.size})}0&&(module.exports={cn,sv});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents valid class name input types
|
|
3
|
+
*/
|
|
4
|
+
type ClassValue = string | ClassArray | ClassRecord | boolean | number | bigint | null | undefined;
|
|
5
|
+
type ClassArray = ClassValue[];
|
|
6
|
+
type ClassRecord = Record<string, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* Constructs a class name string from various input types
|
|
9
|
+
*/
|
|
10
|
+
declare function cn(...args: ClassValue[]): string;
|
|
11
|
+
|
|
12
|
+
type Prettify<T> = {
|
|
13
|
+
[K in keyof T]: T[K];
|
|
14
|
+
} & {};
|
|
15
|
+
type PartialUndefined<T> = {
|
|
16
|
+
[K in keyof T]?: T[K] | undefined;
|
|
17
|
+
};
|
|
18
|
+
type StringKeyof<T> = Extract<keyof T, string>;
|
|
19
|
+
type SVClassValue<S extends SVSlots | undefined> = S extends SVSlots ? SlotsClassValue<S> | ClassValue : ClassValue;
|
|
20
|
+
type ClassProp<S extends SVSlots | undefined = undefined> = {
|
|
21
|
+
class?: SVClassValue<S>;
|
|
22
|
+
className?: never;
|
|
23
|
+
} | {
|
|
24
|
+
class?: never;
|
|
25
|
+
className?: SVClassValue<S>;
|
|
26
|
+
};
|
|
27
|
+
type SVSlots = {
|
|
28
|
+
base?: ClassValue;
|
|
29
|
+
} & Record<string, ClassValue>;
|
|
30
|
+
type BooleanString<T> = T extends 'true' | 'false' ? boolean : T;
|
|
31
|
+
type SVSlotKey<S> = 'base' | StringKeyof<S>;
|
|
32
|
+
type SlotsClassValue<S extends SVSlots> = Partial<Record<SVSlotKey<S>, ClassValue>>;
|
|
33
|
+
type SVVariants<S extends SVSlots | undefined> = Record<string, Record<string | number, SVClassValue<S>> | SVClassValue<S>>;
|
|
34
|
+
type SVVariantConditions<S extends SVSlots | undefined, V extends SVVariants<S> | undefined> = {
|
|
35
|
+
[K in StringKeyof<V>]?: SVVariantPropType<V[K], S> | SVVariantPropType<V[K], S>[] | undefined;
|
|
36
|
+
};
|
|
37
|
+
type SVCompoundVariants<S extends SVSlots | undefined, V extends SVVariants<S> | undefined> = (SVVariantConditions<S, V> & ClassProp<S>)[];
|
|
38
|
+
type SVCompoundSlots<S extends SVSlots | undefined, V extends SVVariants<S> | undefined> = ({
|
|
39
|
+
slots: SVSlotKey<S>[];
|
|
40
|
+
} & SVVariantConditions<S, V> & ClassProp<S>)[];
|
|
41
|
+
type IsBooleanShorthand<T, S extends SVSlots | undefined> = T extends Record<string, unknown> ? [Extract<keyof T, number>] extends [never] ? S extends SVSlots ? StringKeyof<T> extends SVSlotKey<S> ? true : StringKeyof<T> extends 'true' | 'false' ? true : false : StringKeyof<T> extends 'true' | 'false' ? true : false : false : true;
|
|
42
|
+
type SVVariantPropType<T, S extends SVSlots | undefined> = IsBooleanShorthand<T, S> extends true ? boolean : T extends Record<string | number, unknown> ? BooleanString<StringKeyof<T>> | Extract<keyof T, number> : boolean;
|
|
43
|
+
type SVVariantProps<S extends SVSlots | undefined, V extends SVVariants<S> | undefined> = {
|
|
44
|
+
[K in StringKeyof<V>]: SVVariantPropType<V[K], S>;
|
|
45
|
+
};
|
|
46
|
+
type SVDefaultVariantFn<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, K extends StringKeyof<V>> = (props: Partial<SVVariantProps<S, V>>) => SVVariantPropType<V[K], S> | undefined;
|
|
47
|
+
type SVDefaultVariants<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, RV extends StringKeyof<V>[]> = {
|
|
48
|
+
[K in StringKeyof<Omit<SVVariantProps<S, V>, RV[number]>>]?: SVVariantPropType<V[K], S> | SVDefaultVariantFn<S, V, K> | undefined;
|
|
49
|
+
};
|
|
50
|
+
type SVProps<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, RV extends StringKeyof<V>[]> = V extends undefined ? ClassProp<S> : Prettify<Pick<SVVariantProps<S, V>, RV[number]> & Omit<PartialUndefined<SVVariantProps<S, V>>, RV[number]>> & ClassProp<S>;
|
|
51
|
+
type SVConfig<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, RV extends StringKeyof<V>[]> = {
|
|
52
|
+
variants?: V | undefined;
|
|
53
|
+
slots?: S | undefined;
|
|
54
|
+
compoundVariants?: SVCompoundVariants<S, V> | undefined;
|
|
55
|
+
compoundSlots?: SVCompoundSlots<S, V> | undefined;
|
|
56
|
+
defaultVariants?: SVDefaultVariants<S, V, RV> | undefined;
|
|
57
|
+
requiredVariants?: RV | undefined;
|
|
58
|
+
cacheSize?: number | undefined;
|
|
59
|
+
postProcess?: ((className: string) => string) | undefined;
|
|
60
|
+
};
|
|
61
|
+
type SVReturnValue<S extends SVSlots | undefined> = S extends undefined ? string : Prettify<Record<SVSlotKey<S>, string>>;
|
|
62
|
+
type SVReturnFn<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, RV extends StringKeyof<V>[]> = RV extends [] ? (props?: SVProps<S, V, RV> | undefined) => SVReturnValue<S> : (props: SVProps<S, V, RV>) => SVReturnValue<S>;
|
|
63
|
+
type SVReturnType<S extends SVSlots | undefined, V extends SVVariants<S> | undefined, RV extends StringKeyof<V>[]> = SVReturnFn<S, V, RV> & {
|
|
64
|
+
variants: V;
|
|
65
|
+
variantKeys: StringKeyof<V>[];
|
|
66
|
+
slots: S;
|
|
67
|
+
slotKeys: S extends SVSlots ? SVSlotKey<S>[] : ['base'];
|
|
68
|
+
defaultVariants: SVDefaultVariants<S, V, RV>;
|
|
69
|
+
requiredVariants: RV;
|
|
70
|
+
clearCache: () => void;
|
|
71
|
+
getCacheSize: () => number;
|
|
72
|
+
};
|
|
73
|
+
type VariantProps<T extends (...args: any[]) => unknown> = Prettify<Omit<Exclude<Parameters<T>[0], undefined>, 'class' | 'className'>>;
|
|
74
|
+
/**
|
|
75
|
+
* Creates variant-based class name generator with optional slots support.
|
|
76
|
+
* Inspired by CVA and tailwind-variants.
|
|
77
|
+
*/
|
|
78
|
+
declare function sv(base: ClassValue): string;
|
|
79
|
+
declare function sv<S extends SVSlots | undefined = undefined, V extends SVVariants<S> | undefined = undefined, RV extends StringKeyof<V>[] | [] = []>(base: ClassValue, config: SVConfig<S, V, RV>): SVReturnType<S, V, RV>;
|
|
80
|
+
|
|
81
|
+
export { type ClassValue, type VariantProps, cn, sv };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var{isArray:F}=Array,{keys:B}=Object;function x(...S){let o=[...S],a=0,V="";for(;a<o.length;){let r=o[a];if(a++,!!r){if(typeof r=="string")V+=` ${r}`;else if(F(r))o.push(...r);else if(typeof r=="object")for(let y of B(r))r[y]&&(V+=` ${y}`)}}return V.slice(1)}var{isArray:T}=Array,{assign:I,entries:R,keys:c}=Object,$=(S,o)=>S===o||`${S}`==`${o}`,E=(S,o)=>{for(let a of c(o)){if(a==="class"||a==="className"||a==="slots")continue;let V=o[a],r=S[a];if(T(V)){if(!V.some(y=>$(y,r)))return!1}else if(!$(V,r))return!1}return!0};function M(S,o){if(!o)return x(S);let{variants:a={},slots:V={},compoundVariants:r=[],compoundSlots:y=[],defaultVariants:C={},requiredVariants:K=[],cacheSize:z=256,postProcess:j}=o,f=new Map,v=(n,s)=>{if(n){if(f.size>=z){let t=f.keys().next().value;t&&f.delete(t)}f.set(n,s)}return s},{base:N,...h}=V,O=x(S,N),p=new Set(c(h)),g={};for(let[n,s]of R(a))if(s&&typeof s=="object"&&!T(s)){let t=c(s);p.size>0&&t.length>0&&t.every(i=>i==="base"||p.has(i))?g[n]={false:"",true:s}:t.every(i=>i==="true"||i==="false")?g[n]={true:"",false:"",...s}:g[n]=s}else g[n]={false:"",true:s};let q=R(g),w=new Set(c(g)),k=new Set(c(C)),A=n=>j?.(n)??n,D=n=>{if(!n||typeof n!="object"||T(n))return!1;for(let s of c(n))if(!(s==="base"||p.has(s)))return!1;return!0},b=(n,s)=>{if(D(s))for(let[t,u]of R(s))n[t]?.push(u);else n.base.push(s)};for(let n of K){if(!w.has(n))throw new Error(`Required variant "${n}" is not defined in variants`);if(k.has(n))throw new Error(`Required variant "${n}" cannot have a default value`)}return I((n={})=>{let s=n.class??n.className,t={},u="";for(let e of w){let l=n[e];if(l===void 0){if(k.has(e)){let d=C[e];typeof d=="function"?t[e]=d(n):t[e]=d,s||(u+=`${t[e]};`)}}else t[e]=l,s||(u+=`${l};`)}if(!s){let e=f.get(u);if(e)return e}for(let e of K)if(t[e]===void 0)throw new Error(`Missing required variant: "${e}"`);let i={base:[O]};for(let e of p)i[e]=[h[e]];for(let[e,l]of q){let d=t[e];if(d===void 0)continue;let P=l[String(d)];if(P===void 0)throw new Error(`Invalid value "${d}" for variant "${e}"`);P!==""&&b(i,P)}for(let e of r)E(t,e)&&b(i,e.class??e.className);for(let e of y)if(E(t,e)){let l=e.class??e.className;for(let d of e.slots)i[d]?.push(l)}s&&b(i,s);let m={base:""};for(let[e,l]of R(i))m[e]=A(x(l));return p.size===0?v(u,m.base):v(u,m)},{variants:a,variantKeys:c(a),slots:V,slotKeys:["base",...p],defaultVariants:C,requiredVariants:K,clearCache:()=>f.clear(),getCacheSize:()=>f.size})}export{x as cn,M as sv};
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "slot-variants",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Type-safe class name variants with slots",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"require": "./dist/index.cjs"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup src/index.ts && rm dist/index.d.cts",
|
|
18
|
+
"test": "tap --show-full-coverage test/*.test.ts",
|
|
19
|
+
"bench": "jiti bench/cn.ts && jiti bench/cva.ts && jiti bench/tv.ts",
|
|
20
|
+
"lint": "eslint src",
|
|
21
|
+
"format": "prettier --write src",
|
|
22
|
+
"format:check": "prettier --check src",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@eslint/js": "9.39.2",
|
|
30
|
+
"@stylistic/eslint-plugin": "^5.7.1",
|
|
31
|
+
"class-variance-authority": "^0.7.1",
|
|
32
|
+
"classnames": "^2.5.1",
|
|
33
|
+
"eslint": "9.39.2",
|
|
34
|
+
"jiti": "2.6.1",
|
|
35
|
+
"prettier": "3.8.1",
|
|
36
|
+
"tailwind-merge": "^3.4.0",
|
|
37
|
+
"tailwind-variants": "^3.2.2",
|
|
38
|
+
"tap": "21.5.0",
|
|
39
|
+
"tinybench": "^6.0.0",
|
|
40
|
+
"tsup": "8.5.1",
|
|
41
|
+
"typescript": "5.9.3",
|
|
42
|
+
"typescript-eslint": "8.54.0"
|
|
43
|
+
},
|
|
44
|
+
"tsup": {
|
|
45
|
+
"dts": true,
|
|
46
|
+
"format": [
|
|
47
|
+
"cjs",
|
|
48
|
+
"esm"
|
|
49
|
+
],
|
|
50
|
+
"minify": true
|
|
51
|
+
}
|
|
52
|
+
}
|