@codefast/tailwind-variants 0.3.7-canary.1

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/README.md ADDED
@@ -0,0 +1,963 @@
1
+ # Tailwind Variants
2
+
3
+ Type-safe variant API for Tailwind CSS with enhanced functionality and advanced TypeScript support for building flexible component styling systems.
4
+
5
+ [![CI](https://github.com/codefastlabs/codefast/actions/workflows/release.yml/badge.svg)](https://github.com/codefastlabs/codefast/actions/workflows/release.yml)
6
+ [![NPM Version](https://img.shields.io/npm/v/@codefast/tailwind-variants.svg)](https://www.npmjs.com/package/@codefast/tailwind-variants)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.9%2B-blue.svg)](https://www.typescriptlang.org/)
9
+ [![Bundle Size](https://img.shields.io/bundlephobia/minzip/@codefast/tailwind-variants)](https://bundlephobia.com/package/@codefast/tailwind-variants)
10
+
11
+ ## Installation
12
+
13
+ Install the package via pnpm (recommended):
14
+
15
+ ```bash
16
+ pnpm add @codefast/tailwind-variants
17
+ ```
18
+
19
+ Or using npm:
20
+
21
+ ```bash
22
+ npm install @codefast/tailwind-variants
23
+ ```
24
+
25
+ **Peer Dependencies**:
26
+
27
+ The library works with Tailwind CSS (optional but recommended):
28
+
29
+ ```bash
30
+ pnpm add tailwindcss
31
+ ```
32
+
33
+ **Dependencies**:
34
+
35
+ The library uses these runtime dependencies:
36
+
37
+ - `clsx`: Utility for constructing className strings conditionally
38
+ - `tailwind-merge`: Utility for merging Tailwind CSS classes and resolving conflicts
39
+
40
+ **Requirements**:
41
+
42
+ - Node.js version 20.0.0 or higher
43
+ - TypeScript version 5.9.2 or higher (recommended)
44
+ - Tailwind CSS version 4.0.0 or higher (optional)
45
+
46
+ ## Quick Start
47
+
48
+ ```typescript
49
+ import { tv } from "@codefast/tailwind-variants";
50
+
51
+ const button = tv({
52
+ base: "inline-flex items-center justify-center rounded-md font-medium transition-colors",
53
+ variants: {
54
+ variant: {
55
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
56
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
57
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
58
+ },
59
+ size: {
60
+ sm: "h-9 px-3 text-sm",
61
+ md: "h-10 px-4",
62
+ lg: "h-11 px-8",
63
+ },
64
+ },
65
+ defaultVariants: {
66
+ variant: "default",
67
+ size: "md",
68
+ },
69
+ });
70
+
71
+ // Usage
72
+ console.log(button());
73
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4"
74
+
75
+ console.log(button({ variant: "destructive", size: "lg" }));
76
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors bg-destructive text-destructive-foreground hover:bg-destructive/90 h-11 px-8"
77
+ ```
78
+
79
+ ## Usage
80
+
81
+ ### Core Features
82
+
83
+ The library provides a comprehensive set of features for variant-based styling:
84
+
85
+ #### Variant System
86
+
87
+ - **Basic Variants**: Define component variations with different styling options
88
+ - **Boolean Variants**: Support for boolean-based variant conditions
89
+ - **Default Variants**: Set default values for variant properties
90
+ - **Nested Arrays**: Support for nested array structures in class definitions
91
+
92
+ #### Advanced Features
93
+
94
+ - **Slots**: Multi-part component styling with individual slot control
95
+ - **Compound Variants**: Apply styles when multiple variant conditions are met
96
+ - **Compound Slots**: Apply styles to specific slots based on variant conditions
97
+ - **Configuration Extension**: Extend and override existing variant configurations
98
+
99
+ #### Developer Experience
100
+
101
+ - **Type Safety**: Full TypeScript support with intelligent type inference
102
+ - **Tailwind Merge**: Built-in conflict resolution for Tailwind CSS classes
103
+ - **Performance**: Optimized for minimal runtime overhead
104
+ - **Flexibility**: Support for custom class merging and configuration
105
+
106
+ ### Basic Variants
107
+
108
+ Create variant functions with different styling options:
109
+
110
+ ```typescript
111
+ import { tv } from "@codefast/tailwind-variants";
112
+
113
+ const button = tv({
114
+ base: "inline-flex items-center justify-center rounded-md font-medium transition-colors",
115
+ variants: {
116
+ variant: {
117
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
118
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
119
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
120
+ },
121
+ size: {
122
+ sm: "h-9 px-3 text-sm",
123
+ md: "h-10 px-4",
124
+ lg: "h-11 px-8",
125
+ },
126
+ },
127
+ defaultVariants: {
128
+ variant: "default",
129
+ size: "md",
130
+ },
131
+ });
132
+
133
+ // Usage examples
134
+ console.log(button());
135
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4"
136
+
137
+ console.log(button({ variant: "destructive", size: "lg" }));
138
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors bg-destructive text-destructive-foreground hover:bg-destructive/90 h-11 px-8"
139
+
140
+ console.log(button({ variant: "outline", size: "sm" }));
141
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors border border-input bg-background hover:bg-accent hover:text-accent-foreground h-9 px-3 text-sm"
142
+
143
+ // Add custom classes
144
+ console.log(button({ className: "w-full" }));
145
+ // Output: "inline-flex items-center justify-center rounded-md font-medium transition-colors bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 w-full"
146
+ ```
147
+
148
+ ### Slots - Multi-part Components
149
+
150
+ Slots enable styling for components with multiple parts:
151
+
152
+ ```typescript
153
+ import { tv } from "@codefast/tailwind-variants";
154
+
155
+ const card = tv({
156
+ slots: {
157
+ base: "rounded-lg border bg-card text-card-foreground shadow-sm",
158
+ header: "flex flex-col space-y-1.5 p-6",
159
+ content: "p-6 pt-0",
160
+ footer: "flex items-center p-6 pt-0",
161
+ },
162
+ variants: {
163
+ variant: {
164
+ default: "",
165
+ destructive: {
166
+ base: "border-destructive",
167
+ header: "text-destructive",
168
+ },
169
+ },
170
+ },
171
+ });
172
+
173
+ // Usage
174
+ const cardStyles = card();
175
+ console.log(cardStyles.base());
176
+ // Output: "rounded-lg border bg-card text-card-foreground shadow-sm"
177
+
178
+ console.log(cardStyles.header());
179
+ // Output: "flex flex-col space-y-1.5 p-6"
180
+
181
+ console.log(cardStyles.content());
182
+ // Output: "p-6 pt-0"
183
+
184
+ console.log(cardStyles.footer());
185
+ // Output: "flex items-center p-6 pt-0"
186
+
187
+ // With variant
188
+ const destructiveCard = card({ variant: "destructive" });
189
+ console.log(destructiveCard.base());
190
+ // Output: "rounded-lg border bg-card text-card-foreground shadow-sm border-destructive"
191
+
192
+ console.log(destructiveCard.header());
193
+ // Output: "flex flex-col space-y-1.5 p-6 text-destructive"
194
+ ```
195
+
196
+ ### Compound Variants
197
+
198
+ Apply styles when multiple variant conditions are met:
199
+
200
+ ```typescript
201
+ import { tv } from "@codefast/tailwind-variants";
202
+
203
+ const alert = tv({
204
+ base: "relative w-full rounded-lg border px-4 py-3",
205
+ variants: {
206
+ variant: {
207
+ default: "bg-background text-foreground",
208
+ destructive: "border-destructive/50 text-destructive",
209
+ },
210
+ size: {
211
+ sm: "text-sm",
212
+ md: "text-base",
213
+ },
214
+ },
215
+ compoundVariants: [
216
+ {
217
+ variant: "destructive",
218
+ size: "md",
219
+ className: "font-semibold", // Only applies when variant=destructive AND size=md
220
+ },
221
+ ],
222
+ defaultVariants: {
223
+ variant: "default",
224
+ size: "md",
225
+ },
226
+ });
227
+
228
+ // Usage examples
229
+ console.log(alert({ size: "sm" }));
230
+ // Output: "relative w-full rounded-lg border px-4 py-3 bg-background text-foreground text-sm"
231
+
232
+ console.log(alert({ variant: "destructive", size: "md" }));
233
+ // Output: "relative w-full rounded-lg border px-4 py-3 border-destructive/50 text-destructive text-base font-semibold"
234
+
235
+ console.log(alert({ variant: "destructive", size: "sm" }));
236
+ // Output: "relative w-full rounded-lg border px-4 py-3 border-destructive/50 text-destructive text-sm"
237
+ ```
238
+
239
+ ### Boolean Variants
240
+
241
+ Support for boolean-based variant conditions:
242
+
243
+ ```typescript
244
+ import { tv } from "@codefast/tailwind-variants";
245
+
246
+ const toggle = tv({
247
+ base: "inline-flex items-center justify-center rounded-md text-sm font-medium",
248
+ variants: {
249
+ pressed: {
250
+ true: "bg-accent text-accent-foreground",
251
+ false: "bg-transparent",
252
+ },
253
+ disabled: {
254
+ true: "opacity-50 pointer-events-none",
255
+ false: "",
256
+ },
257
+ },
258
+ defaultVariants: {
259
+ pressed: false,
260
+ disabled: false,
261
+ },
262
+ });
263
+
264
+ // Usage with boolean values
265
+ console.log(toggle());
266
+ // Output: "inline-flex items-center justify-center rounded-md text-sm font-medium bg-transparent"
267
+
268
+ console.log(toggle({ pressed: true }));
269
+ // Output: "inline-flex items-center justify-center rounded-md text-sm font-medium bg-accent text-accent-foreground"
270
+
271
+ console.log(toggle({ disabled: true }));
272
+ // Output: "inline-flex items-center justify-center rounded-md text-sm font-medium bg-transparent opacity-50 pointer-events-none"
273
+
274
+ console.log(toggle({ pressed: true, disabled: true }));
275
+ // Output: "inline-flex items-center justify-center rounded-md text-sm font-medium bg-accent text-accent-foreground opacity-50 pointer-events-none"
276
+ ```
277
+
278
+ ### Configuration Extension
279
+
280
+ Extend existing configurations for reusability:
281
+
282
+ ```typescript
283
+ import { tv } from "@codefast/tailwind-variants";
284
+
285
+ // Base button configuration
286
+ const baseButton = tv({
287
+ base: "inline-flex items-center justify-center rounded-md font-medium",
288
+ variants: {
289
+ size: {
290
+ sm: "h-9 px-3 text-sm",
291
+ md: "h-10 px-4",
292
+ },
293
+ },
294
+ defaultVariants: {
295
+ size: "md",
296
+ },
297
+ });
298
+
299
+ // Extended icon button
300
+ const iconButton = tv({
301
+ extend: baseButton,
302
+ base: "aspect-square", // Additional base classes
303
+ variants: {
304
+ variant: {
305
+ // New variant options
306
+ ghost: "hover:bg-accent hover:text-accent-foreground",
307
+ outline: "border border-input",
308
+ },
309
+ },
310
+ defaultVariants: {
311
+ variant: "ghost",
312
+ },
313
+ });
314
+
315
+ // Usage
316
+ console.log(baseButton());
317
+ // Output: "inline-flex items-center justify-center rounded-md font-medium h-10 px-4"
318
+
319
+ console.log(iconButton());
320
+ // Output: "inline-flex items-center justify-center rounded-md font-medium aspect-square h-10 px-4 hover:bg-accent hover:text-accent-foreground"
321
+
322
+ console.log(iconButton({ variant: "outline", size: "sm" }));
323
+ // Output: "inline-flex items-center justify-center rounded-md font-medium aspect-square h-9 px-3 text-sm border border-input"
324
+ ```
325
+
326
+ ## Advanced Features
327
+
328
+ ### Compound Slots
329
+
330
+ Apply styles to specific slots based on variant conditions:
331
+
332
+ ```typescript
333
+ import { tv } from "@codefast/tailwind-variants";
334
+
335
+ const dialog = tv({
336
+ slots: {
337
+ overlay: "fixed inset-0 bg-background/80 backdrop-blur-sm",
338
+ content: "fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2",
339
+ header: "flex flex-col space-y-1.5 text-center sm:text-left",
340
+ footer: "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
341
+ },
342
+ variants: {
343
+ size: {
344
+ sm: "",
345
+ lg: "",
346
+ },
347
+ },
348
+ compoundSlots: [
349
+ {
350
+ size: "sm",
351
+ slots: ["content"],
352
+ className: "max-w-md",
353
+ },
354
+ {
355
+ size: "lg",
356
+ slots: ["content"],
357
+ className: "max-w-2xl",
358
+ },
359
+ ],
360
+ });
361
+
362
+ // Usage
363
+ const smallDialog = dialog({ size: "sm" });
364
+ console.log(smallDialog.content());
365
+ // Output: "fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 max-w-md"
366
+
367
+ const largeDialog = dialog({ size: "lg" });
368
+ console.log(largeDialog.content());
369
+ // Output: "fixed left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 max-w-2xl"
370
+ ```
371
+
372
+ ### Global Configuration with createTV
373
+
374
+ Create a factory with global configuration settings:
375
+
376
+ ```typescript
377
+ import { createTV } from "@codefast/tailwind-variants";
378
+
379
+ // Create factory with global configuration
380
+ const { tv, cn } = createTV({
381
+ twMerge: true,
382
+ twMergeConfig: {
383
+ extend: {
384
+ classGroups: {
385
+ // Custom class groups for better conflict resolution
386
+ "font-size": ["text-custom-sm", "text-custom-lg"],
387
+ },
388
+ },
389
+ },
390
+ });
391
+
392
+ // Use tv with global configuration
393
+ const button = tv({
394
+ base: "px-4 py-2 rounded",
395
+ variants: {
396
+ variant: {
397
+ primary: "bg-blue-500 text-white",
398
+ secondary: "bg-gray-500 text-white",
399
+ },
400
+ },
401
+ });
402
+
403
+ // Use cn utility with global configuration
404
+ const classes = cn("px-4 py-2", "px-6 py-3"); // Tailwind merge resolves conflicts
405
+ console.log(classes);
406
+ // Output: "px-6 py-3" (px-6 overrides px-4, py-3 overrides py-2)
407
+ ```
408
+
409
+ ### Nested Arrays Support
410
+
411
+ Support for nested array structures in class definitions:
412
+
413
+ ```typescript
414
+ import { tv } from "@codefast/tailwind-variants";
415
+
416
+ const component = tv({
417
+ base: ["base-class-1", ["base-class-2", ["base-class-3", "base-class-4"]]], // Automatically flattened
418
+ variants: {
419
+ variant: {
420
+ primary: ["text-blue-500", ["bg-blue-50", ["hover:bg-blue-100", "focus:ring-blue-200"]]],
421
+ },
422
+ },
423
+ });
424
+ ```
425
+
426
+ ## API Reference
427
+
428
+ ### tv(config, options?)
429
+
430
+ Creates a variant function from configuration.
431
+
432
+ **Parameters**:
433
+
434
+ - `config`: Configuration object defining variants, slots, and styling
435
+ - `options?`: Optional Tailwind Variants configuration for customization
436
+
437
+ **Returns**: Configured variant function
438
+
439
+ ### createTV(globalConfig?)
440
+
441
+ Creates a factory with global configuration settings.
442
+
443
+ **Parameters**:
444
+
445
+ - `globalConfig?`: Global Tailwind Variants configuration applied to all instances
446
+
447
+ **Returns**: Object containing `tv` and `cn` functions with global settings
448
+
449
+ ### cn(...classes)
450
+
451
+ Combines and merges CSS classes using tailwind-merge for conflict resolution.
452
+
453
+ **Parameters**:
454
+
455
+ - `...classes`: CSS classes to combine and merge
456
+
457
+ **Returns**: Merged class string with conflicts resolved
458
+
459
+ ### cx(...classes)
460
+
461
+ Combines CSS classes using clsx without conflict resolution.
462
+
463
+ **Parameters**:
464
+
465
+ - `...classes`: CSS classes to combine
466
+
467
+ **Returns**: Combined class string without merging
468
+
469
+ ### VariantProps<T>
470
+
471
+ Extracts variant props type from a variant function for TypeScript integration.
472
+
473
+ **Type Parameter**:
474
+
475
+ - `T`: Variant function type
476
+
477
+ **Returns**: Props type object for component integration
478
+
479
+ ### TypeScript Integration
480
+
481
+ The library provides comprehensive TypeScript support:
482
+
483
+ ```typescript
484
+ import { tv, type VariantProps } from "@codefast/tailwind-variants";
485
+
486
+ const button = tv({
487
+ base: "px-4 py-2 rounded",
488
+ variants: {
489
+ variant: {
490
+ primary: "bg-blue-500 text-white",
491
+ secondary: "bg-gray-500 text-white",
492
+ },
493
+ size: {
494
+ sm: "text-sm",
495
+ lg: "text-lg",
496
+ },
497
+ },
498
+ });
499
+
500
+ // Extract props type
501
+ type ButtonProps = VariantProps<typeof button>;
502
+ // Type: { variant?: "primary" | "secondary"; size?: "sm" | "lg"; className?: string; }
503
+
504
+ // Usage in React components
505
+ interface MyButtonProps extends ButtonProps {
506
+ children: React.ReactNode;
507
+ onClick?: () => void;
508
+ }
509
+
510
+ const MyButton: React.FC<MyButtonProps> = ({
511
+ variant,
512
+ size,
513
+ className,
514
+ children,
515
+ onClick
516
+ }) => {
517
+ return (
518
+ <button
519
+ className={button({ variant, size, className })}
520
+ onClick={onClick}
521
+ >
522
+ {children}
523
+ </button>
524
+ );
525
+ };
526
+ ```
527
+
528
+ ### Slots Type Safety
529
+
530
+ Slots provide full type safety for multi-part components:
531
+
532
+ ```typescript
533
+ import { tv } from "@codefast/tailwind-variants";
534
+
535
+ const card = tv({
536
+ slots: {
537
+ base: "rounded-lg border",
538
+ header: "p-4 border-b",
539
+ content: "p-4",
540
+ },
541
+ variants: {
542
+ variant: {
543
+ default: "",
544
+ elevated: {
545
+ base: "shadow-lg",
546
+ header: "bg-muted",
547
+ },
548
+ },
549
+ },
550
+ });
551
+
552
+ // Type inference for slots
553
+ const styles = card({ variant: "elevated" });
554
+ // styles.base() - available
555
+ // styles.header() - available
556
+ // styles.content() - available
557
+ // styles.nonExistent() - TypeScript error!
558
+ ```
559
+
560
+ ### Configuration Options
561
+
562
+ Customize Tailwind merge behavior and other settings:
563
+
564
+ ```typescript
565
+ import { tv } from "@codefast/tailwind-variants";
566
+
567
+ const component = tv(
568
+ {
569
+ base: "px-4 py-2",
570
+ variants: {
571
+ size: {
572
+ sm: "px-2 py-1",
573
+ lg: "px-6 py-3",
574
+ },
575
+ },
576
+ },
577
+ {
578
+ twMerge: true, // Enable/disable tailwind merge (default: true)
579
+ twMergeConfig: {
580
+ extend: {
581
+ classGroups: {
582
+ // Custom class groups for better conflict resolution
583
+ "font-size": ["text-custom-sm", "text-custom-lg"],
584
+ },
585
+ },
586
+ },
587
+ },
588
+ );
589
+ ```
590
+
591
+ ### Disable Tailwind Merge
592
+
593
+ In some cases, you may want to disable Tailwind merge:
594
+
595
+ ```typescript
596
+ import { tv } from "@codefast/tailwind-variants";
597
+
598
+ const component = tv(
599
+ {
600
+ base: "px-4 py-2",
601
+ variants: {
602
+ size: {
603
+ sm: "px-2 py-1", // Will not merge with base classes
604
+ },
605
+ },
606
+ },
607
+ {
608
+ twMerge: false, // Disable tailwind merge
609
+ },
610
+ );
611
+
612
+ console.log(component({ size: "sm" }));
613
+ // Output: "px-4 py-2 px-2 py-1" (both px classes will be kept)
614
+ ```
615
+
616
+ ## Utility Functions
617
+
618
+ ### cn Function
619
+
620
+ Combine and merge CSS classes with conflict resolution:
621
+
622
+ ```typescript
623
+ import { cn } from "@codefast/tailwind-variants";
624
+
625
+ // Basic usage
626
+ const classes = cn("px-4 py-2", "bg-blue-500", "text-white");
627
+ console.log(classes);
628
+ // Output: "px-4 py-2 bg-blue-500 text-white"
629
+
630
+ // With conflicting classes (tailwind-merge resolves conflicts)
631
+ const conflicting = cn("px-4 py-2", "px-6 py-3");
632
+ console.log(conflicting);
633
+ // Output: "px-6 py-3" (later classes override earlier ones)
634
+
635
+ // With conditional classes
636
+ const conditional = cn(
637
+ "base-class",
638
+ true && "condition-true-class",
639
+ false && "condition-false-class",
640
+ { "object-condition": true },
641
+ );
642
+ console.log(conditional);
643
+ // Output: "base-class condition-true-class object-condition"
644
+ ```
645
+
646
+ ### cx Function
647
+
648
+ Combine CSS classes without conflict resolution:
649
+
650
+ ```typescript
651
+ import { cx } from "@codefast/tailwind-variants";
652
+
653
+ const classes = cx("px-4 py-2", "px-6 py-3");
654
+ console.log(classes);
655
+ // Output: "px-4 py-2 px-6 py-3" (no conflict resolution)
656
+ ```
657
+
658
+ ## Best Practices
659
+
660
+ ### Component Library Pattern
661
+
662
+ Create consistent component libraries:
663
+
664
+ ```typescript
665
+ // components/button.ts
666
+ import { tv, type VariantProps } from "@codefast/tailwind-variants";
667
+
668
+ export const buttonVariants = tv({
669
+ base: "inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background",
670
+ variants: {
671
+ variant: {
672
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
673
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
674
+ outline: "border border-input hover:bg-accent hover:text-accent-foreground",
675
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
676
+ ghost: "hover:bg-accent hover:text-accent-foreground",
677
+ link: "underline-offset-4 hover:underline text-primary",
678
+ },
679
+ size: {
680
+ default: "h-10 py-2 px-4",
681
+ sm: "h-9 px-3 rounded-md",
682
+ lg: "h-11 px-8 rounded-md",
683
+ icon: "h-10 w-10",
684
+ },
685
+ },
686
+ defaultVariants: {
687
+ variant: "default",
688
+ size: "default",
689
+ },
690
+ });
691
+
692
+ export type ButtonProps = VariantProps<typeof buttonVariants>;
693
+
694
+ // components/Button.tsx (React component)
695
+ import React from "react";
696
+ import { buttonVariants, type ButtonProps } from "./button";
697
+
698
+ interface ButtonComponentProps
699
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
700
+ ButtonProps {
701
+ asChild?: boolean;
702
+ }
703
+
704
+ const Button = React.forwardRef<HTMLButtonElement, ButtonComponentProps>(
705
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
706
+ return (
707
+ <button
708
+ className={buttonVariants({ variant, size, className })}
709
+ ref={ref}
710
+ {...props}
711
+ />
712
+ );
713
+ }
714
+ );
715
+
716
+ Button.displayName = "Button";
717
+
718
+ export { Button };
719
+ ```
720
+
721
+ ### Theme System Pattern
722
+
723
+ Create theme systems with global configuration:
724
+
725
+ ```typescript
726
+ // theme/variants.ts
727
+ import { createTV } from "@codefast/tailwind-variants";
728
+
729
+ // Global theme configuration
730
+ const { tv: themeTV, cn: themeCN } = createTV({
731
+ twMerge: true,
732
+ twMergeConfig: {
733
+ extend: {
734
+ classGroups: {
735
+ // Theme-specific class groups
736
+ "theme-color": ["theme-primary", "theme-secondary", "theme-accent"],
737
+ },
738
+ },
739
+ },
740
+ });
741
+
742
+ // Export themed utilities
743
+ export { themeTV as tv, themeCN as cn };
744
+
745
+ // components/themed-button.ts
746
+ import { tv } from "../theme/variants";
747
+
748
+ export const themedButton = tv({
749
+ base: "inline-flex items-center justify-center rounded-md font-medium",
750
+ variants: {
751
+ theme: {
752
+ light: "bg-white text-black border border-gray-200",
753
+ dark: "bg-gray-900 text-white border border-gray-700",
754
+ },
755
+ variant: {
756
+ primary: "theme-primary", // Handled by custom class group
757
+ secondary: "theme-secondary",
758
+ },
759
+ },
760
+ });
761
+ ```
762
+
763
+ ### Responsive Design Pattern
764
+
765
+ Use with responsive classes for mobile-first design:
766
+
767
+ ```typescript
768
+ import { tv } from "@codefast/tailwind-variants";
769
+
770
+ const responsiveCard = tv({
771
+ base: "rounded-lg border bg-card text-card-foreground shadow-sm",
772
+ variants: {
773
+ size: {
774
+ sm: "p-4 sm:p-6",
775
+ md: "p-6 sm:p-8 lg:p-10",
776
+ lg: "p-8 sm:p-10 lg:p-12 xl:p-16",
777
+ },
778
+ layout: {
779
+ stack: "flex flex-col space-y-4",
780
+ grid: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
781
+ },
782
+ },
783
+ compoundVariants: [
784
+ {
785
+ size: "lg",
786
+ layout: "grid",
787
+ className: "lg:grid-cols-4 xl:grid-cols-5",
788
+ },
789
+ ],
790
+ defaultVariants: {
791
+ size: "md",
792
+ layout: "stack",
793
+ },
794
+ });
795
+ ```
796
+
797
+ ### Performance Optimization
798
+
799
+ Optimize for performance:
800
+
801
+ ```typescript
802
+ // ✓ Good: Define variants outside component
803
+ const buttonVariants = tv({
804
+ base: "px-4 py-2 rounded",
805
+ variants: {
806
+ variant: {
807
+ primary: "bg-blue-500 text-white",
808
+ secondary: "bg-gray-500 text-white",
809
+ },
810
+ },
811
+ });
812
+
813
+ const Button = ({ variant, className, ...props }) => {
814
+ return (
815
+ <button
816
+ className={buttonVariants({ variant, className })}
817
+ {...props}
818
+ />
819
+ );
820
+ };
821
+
822
+ // ✗ Bad: Define variants inside component (recreated on every render)
823
+ const Button = ({ variant, className, ...props }) => {
824
+ const buttonVariants = tv({
825
+ base: "px-4 py-2 rounded",
826
+ variants: {
827
+ variant: {
828
+ primary: "bg-blue-500 text-white",
829
+ secondary: "bg-gray-500 text-white",
830
+ },
831
+ },
832
+ });
833
+
834
+ return (
835
+ <button
836
+ className={buttonVariants({ variant, className })}
837
+ {...props}
838
+ />
839
+ );
840
+ };
841
+ ```
842
+
843
+ ## Performance Considerations
844
+
845
+ ### Bundle Size
846
+
847
+ The library is optimized for minimal bundle impact:
848
+
849
+ - Tree-shakeable exports for unused functionality
850
+ - Minimal runtime dependencies
851
+ - Efficient class processing algorithms
852
+
853
+ ### Runtime Performance
854
+
855
+ - Lazy evaluation of variant resolution
856
+ - Efficient class merging algorithms
857
+ - Minimal memory footprint
858
+ - Cached tailwind-merge instances for reuse
859
+
860
+ ## Migration Guide
861
+
862
+ ### From class-variance-authority (cva)
863
+
864
+ Migrating from other variant libraries:
865
+
866
+ ```typescript
867
+ // Before (cva)
868
+ import { cva } from "class-variance-authority";
869
+
870
+ const button = cva("px-4 py-2 rounded", {
871
+ variants: {
872
+ variant: {
873
+ primary: "bg-blue-500 text-white",
874
+ secondary: "bg-gray-500 text-white",
875
+ },
876
+ },
877
+ });
878
+
879
+ // After (@codefast/tailwind-variants)
880
+ import { tv } from "@codefast/tailwind-variants";
881
+
882
+ const button = tv({
883
+ base: "px-4 py-2 rounded",
884
+ variants: {
885
+ variant: {
886
+ primary: "bg-blue-500 text-white",
887
+ secondary: "bg-gray-500 text-white",
888
+ },
889
+ },
890
+ });
891
+ ```
892
+
893
+ ### Key Differences
894
+
895
+ 1. **API Structure**: Uses `base` property instead of first string parameter
896
+ 2. **Slots Support**: Native support for multi-part components
897
+ 3. **Extended Configuration**: Built-in support for configuration extension
898
+ 4. **Enhanced TypeScript**: Improved type inference and safety
899
+ 5. **Tailwind Merge**: Built-in conflict resolution
900
+
901
+ ## Contributing
902
+
903
+ We welcome all contributions! To get started with development:
904
+
905
+ ### Environment Setup
906
+
907
+ 1. Fork this repository
908
+ 2. Clone to your machine: `git clone <your-fork-url>`
909
+ 3. Install dependencies: `pnpm install`
910
+ 4. Create a new branch: `git checkout -b feature/feature-name`
911
+
912
+ ### Development Workflow
913
+
914
+ ```bash
915
+ # Build all packages
916
+ pnpm build:packages
917
+
918
+ # Development mode for tailwind-variants
919
+ pnpm dev --filter=@codefast/tailwind-variants
920
+
921
+ # Run tests
922
+ pnpm test --filter=@codefast/tailwind-variants
923
+
924
+ # Run tests with coverage
925
+ pnpm test:coverage --filter=@codefast/tailwind-variants
926
+
927
+ # Lint and format
928
+ pnpm lint:fix
929
+ pnpm format
930
+ ```
931
+
932
+ ### Adding New Features
933
+
934
+ 1. Implement feature in `src/` directory
935
+ 2. Add comprehensive tests in `tests/` directory
936
+ 3. Update TypeScript types as needed
937
+ 4. Update documentation
938
+ 5. Submit a pull request
939
+
940
+ See details at [CONTRIBUTING.md](../../CONTRIBUTING.md).
941
+
942
+ ## License
943
+
944
+ Distributed under the MIT License. See [LICENSE](../../LICENSE) for more details.
945
+
946
+ ## Contact
947
+
948
+ - npm: [@codefast/tailwind-variants](https://www.npmjs.com/package/@codefast/tailwind-variants)
949
+ - GitHub: [codefastlabs/codefast](https://github.com/codefastlabs/codefast)
950
+ - Issues: [GitHub Issues](https://github.com/codefastlabs/codefast/issues)
951
+ - Documentation: [Component Docs](https://codefast.dev/docs/components)
952
+
953
+ ## Acknowledgments
954
+
955
+ This library is built on top of excellent open-source projects:
956
+
957
+ - **[clsx](https://github.com/lukeed/clsx)** - Utility for constructing className strings
958
+ - **[tailwind-merge](https://github.com/dcastil/tailwind-merge)** - Utility for merging Tailwind CSS classes
959
+ - **[Tailwind CSS](https://tailwindcss.com/)** - Utility-first CSS framework
960
+
961
+ ## Changelog
962
+
963
+ See [CHANGELOG.md](./CHANGELOG.md) for a complete list of changes and version history.