tailwind-to-style 3.2.2 → 3.3.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/README.md +486 -21
- package/dist/className/index.cjs +11020 -0
- package/dist/className/index.esm.js +11015 -0
- package/dist/className/index.esm.js.map +1 -0
- package/dist/core/tws.cjs +1 -1
- package/dist/core/tws.esm.js +1 -1
- package/dist/core/tws.esm.js.map +1 -1
- package/dist/core/twsx.cjs +558 -362
- package/dist/core/twsx.esm.js +558 -362
- package/dist/core/twsx.esm.js.map +1 -1
- package/dist/core/twsxVariants.cjs +561 -364
- package/dist/core/twsxVariants.esm.js +561 -364
- package/dist/core/twsxVariants.esm.js.map +1 -1
- package/dist/cx.cjs +1 -1
- package/dist/cx.esm.js +1 -1
- package/dist/index.cjs +3677 -607
- package/dist/index.d.ts +989 -0
- package/dist/index.esm.js +3660 -608
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/utils/index.cjs +94 -27
- package/dist/utils/index.esm.js +94 -27
- package/dist/utils/index.esm.js.map +1 -1
- package/package.json +6 -1
- package/types/className/index.d.ts +41 -0
- package/types/index.d.ts +989 -0
package/README.md
CHANGED
|
@@ -24,6 +24,8 @@
|
|
|
24
24
|
- [`tws()` — Tailwind to Inline Styles](#tws--tailwind-to-inline-styles)
|
|
25
25
|
- [`twsx()` — CSS-in-JS Engine](#twsx--css-in-js-engine)
|
|
26
26
|
- [`twsxVariants()` — Component Variant System](#twsxvariants--component-variant-system)
|
|
27
|
+
- [`twsxClassName()` — Unified CSS-in-JS](#twsxclassname--unified-css-in-js)
|
|
28
|
+
- [`tw()` — Atomic CSS Classes](#tw--atomic-css-classes)
|
|
27
29
|
- [`cx()` — Conditional Class Builder](#cx--conditional-class-builder)
|
|
28
30
|
- [Configuration & Plugins](#configuration--plugins)
|
|
29
31
|
- [`configure()` — Custom Theme](#configure--custom-theme)
|
|
@@ -52,6 +54,8 @@
|
|
|
52
54
|
| **Full Tailwind Support** | All utilities, responsive, pseudo-states, arbitrary values |
|
|
53
55
|
| **SCSS-like Nesting** | `twsx()` for complex nested selector-based styles |
|
|
54
56
|
| **Variant System** | Type-safe component variants like CVA/tailwind-variants |
|
|
57
|
+
| **Unified CSS-in-JS** | `twsxClassName()` — one API for basic/variants/slots |
|
|
58
|
+
| **Atomic CSS Classes** | `tw()` — reusable classes with hover/responsive support |
|
|
55
59
|
| **Conditional Classes** | Built-in `cx()` utility (like clsx/classnames) |
|
|
56
60
|
| **SSR Support** | Server-side rendering with `startSSR()`/`stopSSR()` |
|
|
57
61
|
| **@css Directive** | Inject raw CSS for vendor-specific or complex properties |
|
|
@@ -389,6 +393,303 @@ btnClass({ 'opacity-50': disabled })
|
|
|
389
393
|
|
|
390
394
|
---
|
|
391
395
|
|
|
396
|
+
### `twsxClassName()` — Unified CSS-in-JS
|
|
397
|
+
|
|
398
|
+
The complete CSS-in-JS solution with automatic mode detection. Generates scoped class names with auto-injected CSS from Tailwind classes.
|
|
399
|
+
|
|
400
|
+
```javascript
|
|
401
|
+
import { twsxClassName, tw } from 'tailwind-to-style'
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Basic Mode** — Simple className generation:
|
|
405
|
+
|
|
406
|
+
```javascript
|
|
407
|
+
// Returns: "btn-a1b2c3d4" (or "btn" if hash: false)
|
|
408
|
+
const button = twsxClassName({
|
|
409
|
+
name: 'btn',
|
|
410
|
+
_: 'px-4 py-2 bg-blue-500 text-white rounded-lg',
|
|
411
|
+
hover: 'bg-blue-600',
|
|
412
|
+
focus: 'ring-2 ring-blue-500',
|
|
413
|
+
active: 'bg-blue-700',
|
|
414
|
+
dark: 'bg-blue-400',
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
// Usage
|
|
418
|
+
<button class="${button}">Click me</button>
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
**Variants Mode** — Component variants like CVA/tailwind-variants:
|
|
422
|
+
|
|
423
|
+
```javascript
|
|
424
|
+
const button = twsxClassName({
|
|
425
|
+
name: 'btn',
|
|
426
|
+
base: 'px-4 py-2 font-medium rounded-lg transition-colors',
|
|
427
|
+
variants: {
|
|
428
|
+
variant: {
|
|
429
|
+
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
430
|
+
secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
431
|
+
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
|
|
432
|
+
},
|
|
433
|
+
size: {
|
|
434
|
+
sm: 'text-sm px-3 py-1.5',
|
|
435
|
+
md: 'text-base px-4 py-2',
|
|
436
|
+
lg: 'text-lg px-6 py-3',
|
|
437
|
+
},
|
|
438
|
+
disabled: {
|
|
439
|
+
true: 'opacity-50 cursor-not-allowed',
|
|
440
|
+
false: 'cursor-pointer',
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
compoundVariants: [
|
|
444
|
+
{ variant: 'primary', size: 'lg', class: 'shadow-lg' },
|
|
445
|
+
],
|
|
446
|
+
defaultVariants: {
|
|
447
|
+
variant: 'primary',
|
|
448
|
+
size: 'md',
|
|
449
|
+
disabled: false,
|
|
450
|
+
},
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
// Usage
|
|
454
|
+
<button class="${button()}">Default</button>
|
|
455
|
+
<button class="${button({ variant: 'secondary', size: 'lg' })}">Large Secondary</button>
|
|
456
|
+
<button class="${button({ disabled: true })}">Disabled</button>
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Slots Mode** — Multi-part components:
|
|
460
|
+
|
|
461
|
+
```javascript
|
|
462
|
+
const card = twsxClassName({
|
|
463
|
+
name: 'card',
|
|
464
|
+
slots: {
|
|
465
|
+
root: 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
466
|
+
header: 'px-6 py-4 border-b border-gray-200',
|
|
467
|
+
title: 'text-xl font-bold text-gray-900',
|
|
468
|
+
body: 'px-6 py-4',
|
|
469
|
+
footer: 'px-6 py-4 border-t border-gray-200',
|
|
470
|
+
},
|
|
471
|
+
variants: {
|
|
472
|
+
variant: {
|
|
473
|
+
elevated: { root: 'shadow-2xl' },
|
|
474
|
+
outlined: { root: 'shadow-none border-2 border-gray-200' },
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
// Usage
|
|
480
|
+
const classes = card({ variant: 'elevated' })
|
|
481
|
+
<div class="${classes.root}">
|
|
482
|
+
<div class="${classes.header}">
|
|
483
|
+
<h2 class="${classes.title}">Card Title</h2>
|
|
484
|
+
</div>
|
|
485
|
+
<div class="${classes.body}">Content here</div>
|
|
486
|
+
<div class="${classes.footer}">Footer</div>
|
|
487
|
+
</div>
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
**Namespace Methods:**
|
|
491
|
+
|
|
492
|
+
| Category | Method | Description |
|
|
493
|
+
|----------|--------|-------------|
|
|
494
|
+
| **Config** | `config(options)` | Set global options (prefix, hash, hashLength) |
|
|
495
|
+
| | `getConfig()` | Get current configuration |
|
|
496
|
+
| **Tokens** | `defineTokens(tokens)` | Define design tokens |
|
|
497
|
+
| | `getTokens()` | Get all defined tokens |
|
|
498
|
+
| | `setToken(path, value)` | Set a single token value |
|
|
499
|
+
| **Themes** | `createTheme(name, tokens)` | Create a named theme |
|
|
500
|
+
| | `setTheme(name)` | Activate a theme |
|
|
501
|
+
| | `getTheme()` | Get active theme name |
|
|
502
|
+
| | `getThemes()` | Get all defined themes |
|
|
503
|
+
| **Extend** | `extend(base, extension)` | Extend an existing config |
|
|
504
|
+
| | `compose(...configs)` | Compose multiple configs |
|
|
505
|
+
| | `merge(...classes)` | Merge class values (like cx) |
|
|
506
|
+
| **Animation** | `defineAnimation(name, config)` | Register custom animation |
|
|
507
|
+
| | `getAnimations()` | Get all registered animations |
|
|
508
|
+
| **SSR** | `getCSS(className)` | Get CSS for a className |
|
|
509
|
+
| | `getAllCSS()` | Get all generated CSS |
|
|
510
|
+
| | `extractCSS()` | Extract as `<style>` tag |
|
|
511
|
+
| **Cache** | `clearCache()` | Clear all caches |
|
|
512
|
+
| | `getCacheStats()` | Get cache statistics |
|
|
513
|
+
| **Utility** | `tw(classes)` | Atomic CSS class generator |
|
|
514
|
+
|
|
515
|
+
```javascript
|
|
516
|
+
// ═══════════════════════════════════════════════════════════════
|
|
517
|
+
// Configuration
|
|
518
|
+
// ═══════════════════════════════════════════════════════════════
|
|
519
|
+
|
|
520
|
+
// Global configuration
|
|
521
|
+
twsxClassName.config({ prefix: 'my-app', hash: false, hashLength: 8 })
|
|
522
|
+
twsxClassName.getConfig() // → { prefix: 'my-app', hash: false, ... }
|
|
523
|
+
|
|
524
|
+
// ═══════════════════════════════════════════════════════════════
|
|
525
|
+
// Design Tokens
|
|
526
|
+
// ═══════════════════════════════════════════════════════════════
|
|
527
|
+
|
|
528
|
+
// Define tokens
|
|
529
|
+
twsxClassName.defineTokens({
|
|
530
|
+
colors: { primary: '#3b82f6', danger: '#ef4444' },
|
|
531
|
+
spacing: { sm: '0.5rem', md: '1rem' },
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
// Get all tokens
|
|
535
|
+
twsxClassName.getTokens() // → { colors: {...}, spacing: {...} }
|
|
536
|
+
|
|
537
|
+
// Set a single token
|
|
538
|
+
twsxClassName.setToken('colors.success', '#10b981')
|
|
539
|
+
|
|
540
|
+
// Use tokens in styles (use $path.to.token syntax)
|
|
541
|
+
const btn = twsxClassName({ _: 'bg-$colors.primary p-$spacing.md' })
|
|
542
|
+
|
|
543
|
+
// ═══════════════════════════════════════════════════════════════
|
|
544
|
+
// Theme System
|
|
545
|
+
// ═══════════════════════════════════════════════════════════════
|
|
546
|
+
|
|
547
|
+
// Create named themes
|
|
548
|
+
twsxClassName.createTheme('dark', { background: '#1f2937', text: '#f9fafb' })
|
|
549
|
+
twsxClassName.createTheme('light', { background: '#ffffff', text: '#111827' })
|
|
550
|
+
|
|
551
|
+
// Switch themes
|
|
552
|
+
twsxClassName.setTheme('dark')
|
|
553
|
+
|
|
554
|
+
// Get current/all themes
|
|
555
|
+
twsxClassName.getTheme() // → 'dark'
|
|
556
|
+
twsxClassName.getThemes() // → { dark: {...}, light: {...} }
|
|
557
|
+
|
|
558
|
+
// ═══════════════════════════════════════════════════════════════
|
|
559
|
+
// Extend & Compose
|
|
560
|
+
// ═══════════════════════════════════════════════════════════════
|
|
561
|
+
|
|
562
|
+
// Extend existing config
|
|
563
|
+
const iconButton = twsxClassName.extend(button, {
|
|
564
|
+
base: 'p-0 aspect-square',
|
|
565
|
+
variants: { size: { sm: 'w-8 h-8', md: 'w-10 h-10' } },
|
|
566
|
+
})
|
|
567
|
+
|
|
568
|
+
// Compose multiple configs
|
|
569
|
+
const combined = twsxClassName.compose(baseStyles, variants, overrides)
|
|
570
|
+
|
|
571
|
+
// Merge classes (like cx)
|
|
572
|
+
twsxClassName.merge('p-4', isActive && 'bg-blue-500', { 'opacity-50': disabled })
|
|
573
|
+
|
|
574
|
+
// ═══════════════════════════════════════════════════════════════
|
|
575
|
+
// Animations
|
|
576
|
+
// ═══════════════════════════════════════════════════════════════
|
|
577
|
+
|
|
578
|
+
// Define custom animation preset
|
|
579
|
+
twsxClassName.defineAnimation('myFade', {
|
|
580
|
+
keyframes: { '0%': { opacity: 0 }, '100%': { opacity: 1 } },
|
|
581
|
+
duration: '300ms',
|
|
582
|
+
timing: 'ease-out',
|
|
583
|
+
})
|
|
584
|
+
|
|
585
|
+
// Get all registered animations
|
|
586
|
+
twsxClassName.getAnimations() // → { myFade: {...}, ... }
|
|
587
|
+
|
|
588
|
+
// Use in config
|
|
589
|
+
const modal = twsxClassName({
|
|
590
|
+
_: 'fixed inset-0',
|
|
591
|
+
animation: 'myFade', // or built-in: 'fadeIn', 'slideUp', etc.
|
|
592
|
+
})
|
|
593
|
+
|
|
594
|
+
// ═══════════════════════════════════════════════════════════════
|
|
595
|
+
// SSR Support
|
|
596
|
+
// ═══════════════════════════════════════════════════════════════
|
|
597
|
+
|
|
598
|
+
// Get CSS for specific className
|
|
599
|
+
twsxClassName.getCSS('btn-a1b2c3d4')
|
|
600
|
+
|
|
601
|
+
// Get all generated CSS
|
|
602
|
+
twsxClassName.getAllCSS()
|
|
603
|
+
|
|
604
|
+
// Extract as style tag (for SSR)
|
|
605
|
+
const styleTag = twsxClassName.extractCSS()
|
|
606
|
+
// → '<style data-twsx-classname>...</style>'
|
|
607
|
+
|
|
608
|
+
// ═══════════════════════════════════════════════════════════════
|
|
609
|
+
// Cache Management
|
|
610
|
+
// ═══════════════════════════════════════════════════════════════
|
|
611
|
+
|
|
612
|
+
// Clear all caches
|
|
613
|
+
twsxClassName.clearCache()
|
|
614
|
+
|
|
615
|
+
// Get cache statistics
|
|
616
|
+
twsxClassName.getCacheStats()
|
|
617
|
+
// → { classNameCacheSize: 42, cssCacheSize: 38, styleRegistrySize: 15 }
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
### `tw()` — Atomic CSS Classes
|
|
623
|
+
|
|
624
|
+
Generates reusable atomic CSS classes from Tailwind utilities. Unlike `tws()` which returns inline styles, `tw()` returns class names with auto-injected CSS — supporting pseudo-classes and responsive breakpoints.
|
|
625
|
+
|
|
626
|
+
```javascript
|
|
627
|
+
import { tw } from 'tailwind-to-style'
|
|
628
|
+
// or: import { twsxClassName } from '...' → twsxClassName.tw(...)
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
**Basic Usage:**
|
|
632
|
+
|
|
633
|
+
```javascript
|
|
634
|
+
// Returns: "tw-flex tw-gap-3 tw-items-center"
|
|
635
|
+
tw('flex gap-3 items-center')
|
|
636
|
+
|
|
637
|
+
// Usage
|
|
638
|
+
<div class="${tw('flex gap-3 items-center')}">
|
|
639
|
+
<span>Item 1</span>
|
|
640
|
+
<span>Item 2</span>
|
|
641
|
+
</div>
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
**With Pseudo-classes:**
|
|
645
|
+
|
|
646
|
+
```javascript
|
|
647
|
+
// Returns: "tw-bg-gray-100 tw-hover-bg-gray-200 tw-focus-ring-2"
|
|
648
|
+
tw('bg-gray-100 hover:bg-gray-200 focus:ring-2')
|
|
649
|
+
|
|
650
|
+
// Unlike tws(), these actually work!
|
|
651
|
+
<div class="${tw('opacity-0 hover:opacity-100 transition-opacity')}">
|
|
652
|
+
Hover to reveal
|
|
653
|
+
</div>
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**With Responsive Breakpoints:**
|
|
657
|
+
|
|
658
|
+
```javascript
|
|
659
|
+
// Returns: "tw-flex tw-flex-col tw-md-flex-row tw-lg-gap-8"
|
|
660
|
+
tw('flex flex-col md:flex-row lg:gap-8')
|
|
661
|
+
|
|
662
|
+
<div class="${tw('grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4')}">
|
|
663
|
+
<!-- Responsive grid -->
|
|
664
|
+
</div>
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
**Combining with twsxClassName:**
|
|
668
|
+
|
|
669
|
+
```javascript
|
|
670
|
+
const button = twsxClassName({
|
|
671
|
+
name: 'btn',
|
|
672
|
+
base: 'px-4 py-2 rounded-lg',
|
|
673
|
+
variants: { color: { primary: 'bg-blue-500', danger: 'bg-red-500' } },
|
|
674
|
+
})
|
|
675
|
+
|
|
676
|
+
// Use tw() for layout, twsxClassName for components
|
|
677
|
+
<div class="${tw('flex gap-3 flex-wrap')}">
|
|
678
|
+
<button class="${button({ color: 'primary' })}">Save</button>
|
|
679
|
+
<button class="${button({ color: 'danger' })}">Delete</button>
|
|
680
|
+
</div>
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
**When to use which:**
|
|
684
|
+
|
|
685
|
+
| Function | Use Case | Example |
|
|
686
|
+
|----------|----------|---------|
|
|
687
|
+
| `tws()` | Quick inline styles, no pseudo | `style="${tws('p-4 bg-blue')}"` |
|
|
688
|
+
| `tw()` | Reusable layout utilities | `class="${tw('flex gap-3 hover:...')}"` |
|
|
689
|
+
| `twsxClassName` | Components with variants | `class="${button({ size: 'lg' })}"` |
|
|
690
|
+
|
|
691
|
+
---
|
|
692
|
+
|
|
392
693
|
## Configuration & Plugins
|
|
393
694
|
|
|
394
695
|
### `configure()` — Custom Theme
|
|
@@ -506,12 +807,49 @@ const fullHtml = `
|
|
|
506
807
|
| `IS_BROWSER` | `true` in browser environment |
|
|
507
808
|
| `IS_SERVER` | `true` in Node.js/server environment |
|
|
508
809
|
|
|
810
|
+
### Advanced SSR Collector
|
|
811
|
+
|
|
812
|
+
For more control over SSR CSS collection:
|
|
813
|
+
|
|
814
|
+
```javascript
|
|
815
|
+
import { createSSRCollector } from 'tailwind-to-style'
|
|
816
|
+
|
|
817
|
+
// Create collector with options
|
|
818
|
+
const ssr = createSSRCollector({
|
|
819
|
+
dedupe: true, // Remove duplicate rules
|
|
820
|
+
minify: true, // Minify output CSS
|
|
821
|
+
sort: true, // Sort by specificity
|
|
822
|
+
})
|
|
823
|
+
|
|
824
|
+
// Render app
|
|
825
|
+
const html = renderToString(<App />)
|
|
826
|
+
|
|
827
|
+
// Extract CSS
|
|
828
|
+
const css = ssr.extractRaw() // Raw CSS string
|
|
829
|
+
const styleTag = ssr.extract({ id: 'ssr-css' }) // <style id="ssr-css">...</style>
|
|
830
|
+
|
|
831
|
+
// Or extract critical CSS (above-the-fold)
|
|
832
|
+
const { critical, rest, stats } = ssr.extractCritical({ maxSize: 14000 })
|
|
833
|
+
// critical: CSS under 14KB limit
|
|
834
|
+
// rest: remaining CSS to lazy-load
|
|
835
|
+
// stats: { criticalSize, criticalCount, totalCount }
|
|
836
|
+
|
|
837
|
+
// Utilities
|
|
838
|
+
ssr.peek() // Preview CSS without stopping
|
|
839
|
+
ssr.count // Number of rules collected
|
|
840
|
+
ssr.uniqueCount // Unique rules (after dedupe)
|
|
841
|
+
ssr.clear(true) // Clear and restart collecting
|
|
842
|
+
ssr.getStats() // { ruleCount, uniqueCount, totalSize, minifiedSize }
|
|
843
|
+
```
|
|
844
|
+
|
|
509
845
|
---
|
|
510
846
|
|
|
511
847
|
## Animation System
|
|
512
848
|
|
|
513
849
|
### Built-in CSS Animations
|
|
514
850
|
|
|
851
|
+
Tailwind animation utilities work out of the box:
|
|
852
|
+
|
|
515
853
|
```javascript
|
|
516
854
|
tws('animate-spin') // → animation: spin 1s linear infinite
|
|
517
855
|
tws('animate-bounce') // → animation: bounce 1s infinite
|
|
@@ -519,32 +857,104 @@ tws('animate-pulse') // → animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) i
|
|
|
519
857
|
tws('animate-ping') // → animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
|
|
520
858
|
```
|
|
521
859
|
|
|
522
|
-
### Web Animations API
|
|
860
|
+
### Programmatic Animations (Web Animations API)
|
|
861
|
+
|
|
862
|
+
For interactive animations with full control:
|
|
523
863
|
|
|
524
864
|
```javascript
|
|
525
|
-
import {
|
|
865
|
+
import { animate, chain, stagger, parallel, transition } from 'tailwind-to-style'
|
|
866
|
+
|
|
867
|
+
// Single element animation
|
|
868
|
+
const ctrl = animate(element, 'fadeIn', { duration: 300 })
|
|
869
|
+
ctrl.pause() // Pause
|
|
870
|
+
ctrl.play() // Resume
|
|
871
|
+
ctrl.reverse() // Reverse
|
|
872
|
+
ctrl.cancel() // Cancel
|
|
873
|
+
await ctrl.finished // Wait for completion
|
|
874
|
+
|
|
875
|
+
// Sequential animations
|
|
876
|
+
await chain(element, ['fadeIn', 'slideUp', 'pulse'])
|
|
877
|
+
|
|
878
|
+
// With custom options per step
|
|
879
|
+
await chain(element, [
|
|
880
|
+
'fadeIn',
|
|
881
|
+
{ name: 'slideUp', delay: 200 },
|
|
882
|
+
{ name: 'pulse', options: { iterations: 2 } }
|
|
883
|
+
])
|
|
884
|
+
|
|
885
|
+
// Staggered animations (lists, grids)
|
|
886
|
+
stagger('.card', 'fadeIn', {
|
|
887
|
+
delay: 50, // 50ms between each
|
|
888
|
+
from: 'start', // 'start' | 'end' | 'center' | 'random'
|
|
889
|
+
onAllComplete: () => console.log('Done!')
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
// Parallel animations on multiple elements
|
|
893
|
+
await parallel([el1, el2, el3], 'fadeIn')
|
|
526
894
|
|
|
527
|
-
//
|
|
528
|
-
|
|
529
|
-
|
|
895
|
+
// Transition between states
|
|
896
|
+
transition(element,
|
|
897
|
+
{ opacity: 0, transform: 'scale(0.9)' }, // from
|
|
898
|
+
{ opacity: 1, transform: 'scale(1)' }, // to
|
|
899
|
+
{ duration: 300, easing: 'ease-out' }
|
|
900
|
+
)
|
|
530
901
|
```
|
|
531
902
|
|
|
532
|
-
###
|
|
903
|
+
### Animation Presets
|
|
533
904
|
|
|
534
905
|
```javascript
|
|
535
|
-
import {
|
|
906
|
+
import { ANIMATION_PRESETS, EASING } from 'tailwind-to-style'
|
|
536
907
|
|
|
537
|
-
//
|
|
538
|
-
|
|
908
|
+
// Available presets
|
|
909
|
+
// fadeIn, fadeOut, slideUp, slideDown, slideLeft, slideRight,
|
|
910
|
+
// zoomIn, zoomOut, bounce, shake, pulse, spin, flipX, flipY,
|
|
911
|
+
// enterScale, exitScale, wiggle, heartbeat
|
|
539
912
|
|
|
540
|
-
//
|
|
541
|
-
|
|
913
|
+
// Easing presets
|
|
914
|
+
// linear, ease, easeIn, easeOut, easeInOut,
|
|
915
|
+
// spring, springLight, springMedium, springHeavy,
|
|
916
|
+
// smooth, smoothIn, smoothOut, bounce, elastic
|
|
917
|
+
```
|
|
542
918
|
|
|
543
|
-
|
|
544
|
-
chainAnimations(element, ['fadeIn', 'slideUp', 'bounceIn'])
|
|
919
|
+
### Custom Keyframes
|
|
545
920
|
|
|
546
|
-
|
|
547
|
-
|
|
921
|
+
```javascript
|
|
922
|
+
import { createKeyframes, clearKeyframes, registerPreset } from 'tailwind-to-style'
|
|
923
|
+
|
|
924
|
+
// Create custom keyframes (auto-injects CSS)
|
|
925
|
+
const animationValue = createKeyframes('myFade', {
|
|
926
|
+
'0%': { opacity: 0, transform: 'translateY(-10px)' },
|
|
927
|
+
'100%': { opacity: 1, transform: 'translateY(0)' }
|
|
928
|
+
}, { duration: 400, easing: 'ease-out' })
|
|
929
|
+
// → "myFade 400ms ease-out forwards"
|
|
930
|
+
|
|
931
|
+
// Register as reusable preset
|
|
932
|
+
registerPreset('myFade', {
|
|
933
|
+
keyframes: [{ opacity: 0 }, { opacity: 1 }],
|
|
934
|
+
options: { duration: 400 }
|
|
935
|
+
})
|
|
936
|
+
|
|
937
|
+
// Use it
|
|
938
|
+
animate(element, 'myFade')
|
|
939
|
+
|
|
940
|
+
// Cleanup
|
|
941
|
+
clearKeyframes()
|
|
942
|
+
```
|
|
943
|
+
|
|
944
|
+
### Animation Utilities
|
|
945
|
+
|
|
946
|
+
```javascript
|
|
947
|
+
import {
|
|
948
|
+
cancelAllAnimations,
|
|
949
|
+
getActiveAnimationCount,
|
|
950
|
+
isAnimationSupported,
|
|
951
|
+
getPresetNames
|
|
952
|
+
} from 'tailwind-to-style'
|
|
953
|
+
|
|
954
|
+
cancelAllAnimations() // Stop all running animations
|
|
955
|
+
getActiveAnimationCount() // Number of active animations
|
|
956
|
+
isAnimationSupported() // Check Web Animations API support
|
|
957
|
+
getPresetNames() // List all available preset names
|
|
548
958
|
```
|
|
549
959
|
|
|
550
960
|
---
|
|
@@ -555,26 +965,81 @@ Import only what you need to reduce bundle size by **50-70%**:
|
|
|
555
965
|
|
|
556
966
|
```javascript
|
|
557
967
|
// Individual imports (recommended for production)
|
|
558
|
-
import { tws } from 'tailwind-to-style/tws'
|
|
559
|
-
import { twsx } from 'tailwind-to-style/twsx'
|
|
560
|
-
import { twsxVariants } from 'tailwind-to-style/twsx-variants'
|
|
561
|
-
import {
|
|
968
|
+
import { tws } from 'tailwind-to-style/tws' // ~3KB
|
|
969
|
+
import { twsx } from 'tailwind-to-style/twsx' // ~6KB
|
|
970
|
+
import { twsxVariants } from 'tailwind-to-style/twsx-variants' // ~6KB
|
|
971
|
+
import { twsxClassName, tw } from 'tailwind-to-style/classname' // ~8KB
|
|
972
|
+
import { cx } from 'tailwind-to-style/cx' // <1KB
|
|
562
973
|
|
|
563
974
|
// Full import (everything)
|
|
564
|
-
import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style' // ~
|
|
975
|
+
import { tws, twsx, twsxVariants, twsxClassName, cx } from 'tailwind-to-style' // ~15KB
|
|
565
976
|
```
|
|
566
977
|
|
|
567
978
|
| Import Path | Includes | Size (minified) |
|
|
568
979
|
|---|---|---|
|
|
569
|
-
| `tailwind-to-style` | Everything | ~
|
|
980
|
+
| `tailwind-to-style` | Everything | ~15KB |
|
|
570
981
|
| `tailwind-to-style/tws` | `tws()` only | ~3KB |
|
|
571
982
|
| `tailwind-to-style/twsx` | `twsx()` | ~6KB |
|
|
572
983
|
| `tailwind-to-style/twsx-variants` | `twsxVariants()` | ~6KB |
|
|
984
|
+
| `tailwind-to-style/classname` | `twsxClassName()`, `tw()` | ~8KB |
|
|
573
985
|
| `tailwind-to-style/cx` | `cx()` | <1KB |
|
|
574
986
|
| `tailwind-to-style/utils` | Logger, LRUCache, error handler | ~2KB |
|
|
575
987
|
|
|
576
988
|
All sub-paths provide ESM + CJS bundles with TypeScript type definitions.
|
|
577
989
|
|
|
990
|
+
### Utility Exports
|
|
991
|
+
|
|
992
|
+
```javascript
|
|
993
|
+
import {
|
|
994
|
+
// Debounced versions (for rapid updates)
|
|
995
|
+
debouncedTws, // Debounced tws() - 50ms delay
|
|
996
|
+
debouncedTwsx, // Debounced twsx() - 100ms delay
|
|
997
|
+
|
|
998
|
+
// Performance utilities
|
|
999
|
+
performanceUtils,
|
|
1000
|
+
|
|
1001
|
+
// Error handling
|
|
1002
|
+
TwsError,
|
|
1003
|
+
onError,
|
|
1004
|
+
handleError,
|
|
1005
|
+
|
|
1006
|
+
// Cache utilities
|
|
1007
|
+
LRUCache,
|
|
1008
|
+
getTailwindCache,
|
|
1009
|
+
resetTailwindCache,
|
|
1010
|
+
|
|
1011
|
+
// Logging
|
|
1012
|
+
logger,
|
|
1013
|
+
Logger,
|
|
1014
|
+
} from 'tailwind-to-style'
|
|
1015
|
+
|
|
1016
|
+
// Debounced functions - useful for user input
|
|
1017
|
+
const handleInput = (value) => {
|
|
1018
|
+
debouncedTws(`w-[${value}px]`, true) // Won't spam during typing
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
// Error handling
|
|
1022
|
+
const unsubscribe = onError((error) => {
|
|
1023
|
+
console.error('TWS Error:', error.message, error.context)
|
|
1024
|
+
sendToErrorTracking(error)
|
|
1025
|
+
})
|
|
1026
|
+
|
|
1027
|
+
// Custom error
|
|
1028
|
+
const error = handleError(new Error('Invalid class'), { className: 'invalid' })
|
|
1029
|
+
// → TwsError with context and timestamp
|
|
1030
|
+
|
|
1031
|
+
// LRU Cache (bounded Map)
|
|
1032
|
+
const cache = new LRUCache(1000) // Max 1000 items
|
|
1033
|
+
cache.set('key', 'value')
|
|
1034
|
+
cache.get('key') // → 'value'
|
|
1035
|
+
cache.has('key') // → true
|
|
1036
|
+
cache.size // → 1
|
|
1037
|
+
|
|
1038
|
+
// Logger
|
|
1039
|
+
logger.setLevel('debug') // 'debug' | 'info' | 'warn' | 'error' | 'silent'
|
|
1040
|
+
logger.debug('Processing class:', className)
|
|
1041
|
+
```
|
|
1042
|
+
|
|
578
1043
|
---
|
|
579
1044
|
|
|
580
1045
|
## Preflight CSS
|