tailwind-to-style 3.1.2 → 3.2.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 CHANGED
@@ -5,234 +5,391 @@
5
5
  [![npm version](https://img.shields.io/npm/v/tailwind-to-style.svg)](https://www.npmjs.com/package/tailwind-to-style)
6
6
  [![Build Status](https://github.com/Bigetion/tailwind-to-style/workflows/CI%2FCD/badge.svg)](https://github.com/Bigetion/tailwind-to-style/actions)
7
7
  [![npm downloads](https://img.shields.io/npm/dm/tailwind-to-style.svg)](https://www.npmjs.com/package/tailwind-to-style)
8
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/tailwind-to-style)](https://bundlephobia.com/package/tailwind-to-style)
8
9
  [![license](https://img.shields.io/npm/l/tailwind-to-style.svg)](https://github.com/Bigetion/tailwind-to-style/blob/main/LICENSE)
9
10
 
10
- > **Runtime Tailwind CSS to inline styles converter**
11
- >
12
- > Simple, fast, framework-agnostic. Convert Tailwind utility classes to inline styles or CSS at runtime with zero build step.
11
+ > **Runtime Tailwind CSS to inline styles converter.**
12
+ > Zero build step. SSR-ready. Tree-shakeable. Works everywhere — React, Vue, Svelte, Node.js, vanilla JS.
13
13
 
14
- ## ⚡ Why tailwind-to-style?
14
+ ---
15
15
 
16
- - **🚀 Zero Build Step** - No PostCSS, no compilation, just JavaScript
17
- - **📦 Framework Agnostic** - Works with React, Vue, Svelte, vanilla JS
18
- - **🎨 Full Tailwind Support** - All utilities, responsive, pseudo-states, arbitrary values
19
- - **🔥 SCSS-like Nesting** - Write complex nested styles with ease
20
- - **⚙️ Customizable** - Extend theme with your colors, spacing, fonts
21
- - **💪 TypeScript Support** - Full type definitions included
22
- - **🪶 Lightweight** - ~12KB minified (70% smaller than v2)
16
+ ## Table of Contents
17
+
18
+ - [Why tailwind-to-style?](#why-tailwind-to-style)
19
+ - [Installation](#installation)
20
+ - [Quick Start](#quick-start)
21
+ - [Core API](#core-api)
22
+ - [`tws()` Tailwind to Inline Styles](#tws--tailwind-to-inline-styles)
23
+ - [`twsx()` — CSS-in-JS Engine](#twsx--css-in-js-engine)
24
+ - [`twsxVariants()` — Component Variant System](#twsxvariants--component-variant-system)
25
+ - [`cx()` — Conditional Class Builder](#cx--conditional-class-builder)
26
+ - [Configuration & Plugins](#configuration--plugins)
27
+ - [`configure()` — Custom Theme](#configure--custom-theme)
28
+ - [Plugin System](#plugin-system)
29
+ - [SSR (Server-Side Rendering)](#ssr-server-side-rendering)
30
+ - [Animation System](#animation-system)
31
+ - [Tree-Shakeable Imports](#tree-shakeable-imports)
32
+ - [Preflight CSS](#preflight-css)
33
+ - [Framework Integration](#framework-integration)
34
+ - [Performance](#performance)
35
+ - [Debugging & Logging](#debugging--logging)
36
+ - [Comparison](#comparison)
37
+ - [Migration from v2](#migration-from-v2)
38
+ - [Contributing](#contributing)
39
+ - [Support](#-support)
40
+ - [License](#license)
23
41
 
24
- ## 📥 Installation
42
+ ---
25
43
 
26
- ```bash
27
- npm install tailwind-to-style
28
- ```
44
+ ## Why tailwind-to-style?
45
+
46
+ | Feature | Description |
47
+ |---|---|
48
+ | **Zero Build Step** | No PostCSS, no compilation — just JavaScript |
49
+ | **Framework Agnostic** | React, Vue, Svelte, vanilla JS |
50
+ | **Full Tailwind Support** | All utilities, responsive, pseudo-states, arbitrary values |
51
+ | **SCSS-like Nesting** | `twsx()` for complex nested selector-based styles |
52
+ | **Variant System** | Type-safe component variants like CVA/tailwind-variants |
53
+ | **Conditional Classes** | Built-in `cx()` utility (like clsx/classnames) |
54
+ | **SSR Support** | Server-side rendering with `startSSR()`/`stopSSR()` |
55
+ | **@css Directive** | Inject raw CSS for vendor-specific or complex properties |
56
+ | **Customizable** | Extend theme with colors, spacing, fonts, plugins |
57
+ | **TypeScript** | Full type definitions with generics for autocomplete |
58
+ | **Tree-Shakeable** | Import only what you need — reduce bundle by 50-70% |
59
+ | **Lightweight** | ~12KB minified, zero runtime dependencies |
60
+ | **Lightning Fast** | Pre-compiled regex + multi-level LRU caching |
29
61
 
30
- ## 🎯 Quick Start
62
+ ---
31
63
 
32
- ### Simple Conversion with `tws()`
64
+ ## Installation
33
65
 
34
- Convert Tailwind classes to style objects:
66
+ ```bash
67
+ npm install tailwind-to-style
68
+ ```
35
69
 
36
- ```javascript
37
- import { tws } from 'tailwind-to-style'
70
+ ```bash
71
+ yarn add tailwind-to-style
72
+ ```
38
73
 
39
- const styles = tws('bg-blue-500 text-white p-4 rounded-lg hover:bg-blue-600')
74
+ ```bash
75
+ pnpm add tailwind-to-style
76
+ ```
40
77
 
41
- // Use in React
42
- <div style={styles}>Hello World</div>
78
+ **CDN (browser):**
43
79
 
44
- // Use in vanilla JS
45
- element.style = Object.assign(element.style, styles)
80
+ ```html
81
+ <script src="https://unpkg.com/tailwind-to-style"></script>
82
+ <script>
83
+ const { tws, twsx } = tailwindToStyle
84
+ </script>
46
85
  ```
47
86
 
48
- ### Nested Styles with `twsx()`
87
+ ---
49
88
 
50
- Create complex styles with SCSS-like nesting:
89
+ ## Quick Start
51
90
 
52
91
  ```javascript
53
- import { twsx } from 'tailwind-to-style'
92
+ import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style'
93
+
94
+ // 1. Inline styles
95
+ const style = tws('bg-blue-500 text-white p-4 rounded-lg', true)
96
+ // → { backgroundColor: '#3b82f6', color: '#fff', padding: '1rem', borderRadius: '0.5rem' }
97
+
98
+ // 2. Real CSS with selectors
99
+ twsx({
100
+ '.card': ['bg-white p-6 rounded-xl shadow-md', {
101
+ '&:hover': 'shadow-xl',
102
+ '> .title': 'text-xl font-bold text-gray-900',
103
+ }]
104
+ })
105
+ // → auto-injects <style> with .card { ... } .card:hover { ... }
54
106
 
55
- const css = twsx({
56
- '.card': [
57
- 'bg-white p-6 rounded-lg shadow-md',
58
- {
59
- '&:hover': 'shadow-xl transform scale-105',
60
- '> .title': 'text-2xl font-bold text-gray-900 mb-2',
61
- '> .description': 'text-gray-600 leading-relaxed',
62
- '> .button': [
63
- 'bg-blue-500 text-white px-4 py-2 rounded',
64
- {
65
- '&:hover': 'bg-blue-600',
66
- '&:active': 'bg-blue-700'
67
- }
68
- ]
69
- }
70
- ],
71
-
72
- // Media queries at root level
73
- '@media (max-width: 768px)': {
74
- '.card': 'p-4',
75
- '.card > .title': 'text-xl'
76
- }
107
+ // 3. Component variants
108
+ const btn = twsxVariants('.btn', {
109
+ base: 'px-4 py-2 rounded-lg font-medium',
110
+ variants: {
111
+ color: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },
112
+ size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
113
+ },
114
+ defaultVariants: { color: 'primary', size: 'md' },
77
115
  })
116
+ btn({ color: 'danger', size: 'lg' }) // → "btn btn-danger-lg"
78
117
 
79
- // Inject to document
80
- const style = document.createElement('style')
81
- style.textContent = css
82
- document.head.appendChild(style)
118
+ // 4. Conditional classes
119
+ cx('p-4', isActive && 'bg-blue-500', { 'opacity-50': isDisabled })
120
+ // 'p-4 bg-blue-500'
83
121
  ```
84
122
 
85
- ## 📚 Core API
123
+ ---
124
+
125
+ ## Core API
86
126
 
87
- ### `tws(classes, options?)`
127
+ ### `tws()` — Tailwind to Inline Styles
88
128
 
89
- Convert Tailwind classes to inline style object.
129
+ Converts Tailwind CSS class strings into CSS string or JSON style objects at runtime.
90
130
 
91
131
  ```javascript
92
132
  import { tws } from 'tailwind-to-style'
93
133
 
94
- // Basic usage
95
- const styles = tws('flex items-center gap-4')
134
+ // CSS string (default)
135
+ tws('bg-blue-500 p-4 rounded-lg')
136
+ // → "background-color: #3b82f6; padding: 1rem; border-radius: 0.5rem;"
137
+
138
+ // JSON object (pass `true` as 2nd argument)
139
+ tws('flex items-center gap-4', true)
96
140
  // → { display: 'flex', alignItems: 'center', gap: '1rem' }
141
+ ```
97
142
 
98
- // Responsive classes
99
- const styles = tws('text-sm md:text-base lg:text-lg')
143
+ **Supported features:**
144
+
145
+ ```javascript
146
+ // Responsive breakpoints
147
+ tws('text-sm md:text-base lg:text-lg')
100
148
 
101
149
  // Pseudo-states
102
- const styles = tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
150
+ tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
103
151
 
104
152
  // Arbitrary values
105
- const styles = tws('w-[123px] text-[#abc] m-[1.5rem]')
153
+ tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')
106
154
 
107
155
  // Important modifier
108
- const styles = tws('!bg-red-500')
156
+ tws('!bg-red-500 !text-white')
157
+
158
+ // Negative values
159
+ tws('-mt-4 -translate-x-2')
160
+
161
+ // Opacity modifier
162
+ tws('bg-blue-500/50 text-black/75')
109
163
 
110
- // Return as JSON string
111
- const json = tws('p-4 m-2', { format: 'json' })
112
- // → '{"padding":"1rem","margin":"0.5rem"}'
164
+ // Decimal spacing
165
+ tws('p-0.5 m-1.5 gap-2.5')
113
166
  ```
114
167
 
115
- ### `twsx(styleObject, options?)`
168
+ **Use in React:**
116
169
 
117
- Generate CSS from nested style definitions with Tailwind classes.
170
+ ```jsx
171
+ <div style={tws('flex items-center gap-4 bg-white p-6 rounded-xl shadow-md', true)}>
172
+ <h1 style={tws('text-2xl font-bold text-gray-900', true)}>Hello</h1>
173
+ </div>
174
+ ```
175
+
176
+ ---
177
+
178
+ ### `twsx()` — CSS-in-JS Engine
179
+
180
+ Generates real CSS from Tailwind classes with full selector support, SCSS-like nesting, and auto-injects a `<style>` tag into the DOM.
118
181
 
119
182
  ```javascript
120
183
  import { twsx } from 'tailwind-to-style'
121
184
 
122
- const css = twsx({
185
+ twsx({
123
186
  '.button': [
124
187
  'bg-blue-500 text-white px-6 py-3 rounded-lg font-medium transition-all',
125
188
  {
126
- '&:hover': 'bg-blue-600 transform scale-105',
189
+ '&:hover': 'bg-blue-600 shadow-lg transform scale-105',
127
190
  '&:active': 'bg-blue-700 scale-95',
128
191
  '&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
129
192
  '&.large': 'px-8 py-4 text-lg',
130
- '&.small': 'px-3 py-1.5 text-sm'
131
193
  }
132
194
  ],
133
-
195
+
134
196
  '.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
135
197
  '.card > .header': 'p-6 border-b border-gray-200',
136
198
  '.card > .body': 'p-6',
137
- '.card > .footer': 'p-6 bg-gray-50',
138
-
139
- // Media queries at root level
199
+
200
+ // Media queries
140
201
  '@media (max-width: 768px)': {
141
202
  '.card': 'rounded-lg',
142
203
  '.card > .header': 'p-4',
143
- '.card > .body': 'p-4'
144
204
  }
145
205
  })
206
+ ```
207
+
208
+ **Nesting syntax:**
146
209
 
147
- // Options
148
- const minified = twsx(styles, { minify: true })
149
- const formatted = twsx(styles, { format: 'pretty' })
210
+ | Pattern | Example | Description |
211
+ |---|---|---|
212
+ | `&:pseudo` | `'&:hover': 'bg-blue-600'` | Pseudo-classes |
213
+ | `&.modifier` | `'&.active': 'ring-2'` | Class modifiers |
214
+ | `> .child` | `'> .title': 'text-xl'` | Direct children |
215
+ | `.descendant` | `'.icon': 'w-5 h-5'` | Descendants |
216
+ | `@media` | `'@media (max-width: 768px)': { ... }` | Media queries |
217
+
218
+ **Options:**
219
+
220
+ ```javascript
221
+ // Disable auto-injection (returns CSS string only)
222
+ const css = twsx({ '.btn': 'bg-blue-500 text-white' }, { inject: false })
150
223
  ```
151
224
 
152
- **Nesting Syntax:**
153
- - `'&:hover'` - Pseudo-classes
154
- - `'&.class'` - Modifiers
155
- - `'> .child'` - Direct children
156
- - `'.nested'` - Descendants
157
- - `'@media ...'` - Media queries (root level only)
225
+ #### `@css` Directive — Raw CSS Escape Hatch
158
226
 
159
- ### `twsxVariants(className, config)`
227
+ For CSS that Tailwind can't express, use the `@css` directive:
160
228
 
161
- Create variant-based component styles with automatic CSS generation. Similar to `tailwind-variants` but with auto-injection.
229
+ **String form:**
230
+
231
+ ```javascript
232
+ twsx({
233
+ '.gradient-text': '@css { background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
234
+ })
235
+ ```
236
+
237
+ **Object form (within arrays):**
238
+
239
+ ```javascript
240
+ twsx({
241
+ '.gradient-text': [
242
+ 'text-3xl font-bold',
243
+ {
244
+ '@css': {
245
+ 'background': 'linear-gradient(90deg, #ff6b6b, #feca57)',
246
+ '-webkit-background-clip': 'text',
247
+ '-webkit-text-fill-color': 'transparent',
248
+ },
249
+ },
250
+ ],
251
+ })
252
+ ```
253
+
254
+ ---
255
+
256
+ ### `twsxVariants()` — Component Variant System
257
+
258
+ A CVA-like API for building type-safe component variants. Auto-generates CSS for all combinations and returns a class name builder function.
162
259
 
163
260
  ```javascript
164
261
  import { twsxVariants } from 'tailwind-to-style'
165
262
 
166
263
  const btn = twsxVariants('.btn', {
167
- base: 'px-4 py-2 rounded-lg font-medium transition-all',
264
+ base: 'px-4 py-2 rounded-lg font-medium transition-all border',
168
265
  variants: {
169
266
  variant: {
170
- solid: 'border-transparent',
267
+ solid: 'shadow-sm',
171
268
  outline: 'bg-transparent border-2',
172
- ghost: 'bg-transparent',
269
+ ghost: 'bg-transparent border-transparent',
173
270
  },
174
271
  color: {
175
- primary: 'bg-blue-500 text-white hover:bg-blue-600',
176
- danger: 'bg-red-500 text-white hover:bg-red-600',
272
+ primary: 'bg-blue-500 text-white border-blue-500',
273
+ danger: 'bg-red-500 text-white border-red-500',
274
+ neutral: 'bg-gray-100 text-gray-900 border-gray-300',
177
275
  },
178
276
  size: {
179
277
  sm: 'px-3 py-1.5 text-sm',
180
278
  md: 'px-4 py-2 text-base',
181
279
  lg: 'px-6 py-3 text-lg',
182
280
  },
281
+ disabled: {
282
+ true: 'opacity-50 cursor-not-allowed pointer-events-none',
283
+ },
183
284
  },
184
285
  compoundVariants: [
185
- { variant: 'outline', color: 'primary', class: 'border-blue-500 text-blue-600' },
186
- { variant: 'outline', color: 'danger', class: 'border-red-500 text-red-600' },
286
+ { variant: 'outline', color: 'primary', class: 'bg-transparent text-blue-600 border-blue-500' },
287
+ { variant: 'outline', color: 'danger', class: 'bg-transparent text-red-600 border-red-500' },
187
288
  ],
188
- defaultVariants: { variant: 'solid', color: 'primary', size: 'md' }
289
+ defaultVariants: { variant: 'solid', color: 'primary', size: 'md' },
189
290
  })
190
291
 
191
- // Usage - returns class name string
292
+ // Usage returns class name string
192
293
  btn() // "btn"
193
294
  btn({ color: 'danger' }) // "btn btn-danger"
194
295
  btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
195
-
196
- // In React
197
- const Button = ({ variant, color, size, children, ...props }) => (
198
- <button className={btn({ variant, color, size })} {...props}>
199
- {children}
200
- </button>
201
- )
202
296
  ```
203
297
 
204
- **Nested Selectors** - Style child elements:
298
+ **Nested selectors** style child elements:
205
299
 
206
300
  ```javascript
207
301
  const alert = twsxVariants('.alert', {
208
302
  base: 'p-4 rounded-lg border flex gap-3',
209
303
  variants: {
210
304
  status: {
211
- info: 'bg-blue-50 text-blue-800',
212
- error: 'bg-red-50 text-red-800',
305
+ info: 'bg-blue-50 border-blue-200 text-blue-800',
306
+ error: 'bg-red-50 border-red-200 text-red-800',
213
307
  },
214
308
  },
215
309
  defaultVariants: { status: 'info' },
216
310
  nested: {
217
311
  '.alert-icon': 'flex-shrink-0 mt-0.5',
218
312
  '.alert-content': 'flex-1',
219
- '.alert-dismiss': 'p-1 rounded hover:bg-black/10',
313
+ '.alert-dismiss': 'p-1 rounded hover:bg-black/10 cursor-pointer',
220
314
  }
221
315
  })
316
+ // Generates: .alert .alert-icon { ... }, .alert .alert-content { ... }, etc.
317
+ ```
318
+
319
+ **Class naming convention:**
320
+
321
+ | Call | Returns | Why |
322
+ |---|---|---|
323
+ | `btn()` | `"btn"` | All defaults |
324
+ | `btn({ color: 'danger' })` | `"btn btn-danger"` | One non-default |
325
+ | `btn({ variant: 'outline', color: 'danger', size: 'lg' })` | `"btn btn-outline-danger-lg"` | All non-defaults |
326
+
327
+ **TypeScript — full generics support:**
328
+
329
+ ```typescript
330
+ import { twsxVariants, type VariantProps } from 'tailwind-to-style'
331
+
332
+ const button = twsxVariants('.btn', {
333
+ base: 'px-4 py-2 rounded',
334
+ variants: {
335
+ variant: { solid: 'bg-blue-500', outline: 'border-2' },
336
+ size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
337
+ },
338
+ defaultVariants: { variant: 'solid', size: 'md' },
339
+ })
222
340
 
223
- // Generates CSS:
224
- // .alert .alert-icon { ... }
225
- // .alert .alert-content { ... }
341
+ type ButtonProps = VariantProps<typeof button>
342
+ // { variant?: 'solid' | 'outline', size?: 'sm' | 'md' | 'lg' }
226
343
  ```
227
344
 
228
- **Class Naming Convention:**
229
- - `.btn` = all defaults
230
- - `.btn-outline` = outline variant (non-default)
231
- - `.btn-outline-danger-lg` = multiple non-defaults
345
+ ---
232
346
 
233
- ### `configure(config)`
347
+ ### `cx()` — Conditional Class Builder
234
348
 
235
- Customize theme with your colors, spacing, fonts, and more.
349
+ A built-in utility for conditionally joining class names — replaces `clsx`/`classnames`:
350
+
351
+ ```javascript
352
+ import { cx } from 'tailwind-to-style'
353
+
354
+ // Strings
355
+ cx('bg-blue-500', 'text-white')
356
+ // → 'bg-blue-500 text-white'
357
+
358
+ // Conditionals
359
+ cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
360
+ // → 'p-4 bg-blue-500'
361
+
362
+ // Object syntax
363
+ cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled })
364
+ // → 'p-4 bg-blue-500'
365
+
366
+ // Arrays
367
+ cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
368
+ // → 'p-4 bg-white ring-2 ring-blue-500'
369
+
370
+ // Combined with tws()
371
+ const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))
372
+ ```
373
+
374
+ **`cx.with()` — Base class factory:**
375
+
376
+ ```javascript
377
+ const btnClass = cx.with('px-4 py-2 rounded font-medium transition-colors')
378
+
379
+ btnClass('bg-blue-500 text-white')
380
+ // → 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
381
+
382
+ btnClass({ 'opacity-50': disabled })
383
+ // → 'px-4 py-2 rounded font-medium transition-colors opacity-50'
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Configuration & Plugins
389
+
390
+ ### `configure()` — Custom Theme
391
+
392
+ Extend the default Tailwind theme with custom colors, spacing, fonts, and more.
236
393
 
237
394
  ```javascript
238
395
  import { configure } from 'tailwind-to-style'
@@ -246,201 +403,215 @@ configure({
246
403
  100: '#dbeafe',
247
404
  500: '#3b82f6',
248
405
  600: '#2563eb',
249
- 900: '#1e3a8a'
406
+ 900: '#1e3a8a',
250
407
  },
251
- accent: '#f59e0b'
408
+ accent: '#f59e0b',
252
409
  },
253
410
  spacing: {
254
411
  '128': '32rem',
255
- '144': '36rem'
412
+ '144': '36rem',
256
413
  },
257
414
  fontFamily: {
258
415
  sans: ['Inter', 'system-ui', 'sans-serif'],
259
- mono: ['Fira Code', 'monospace']
260
- }
261
- }
262
- }
416
+ },
417
+ },
418
+ },
263
419
  })
264
420
 
265
- // Now use your custom theme
266
- const styles = tws('bg-brand-500 text-brand-50 p-128 font-sans')
421
+ // Now use custom values
422
+ tws('bg-brand-500 text-brand-50 p-128 font-sans')
267
423
  ```
268
424
 
269
- **Configuration File:**
425
+ **Config API:**
270
426
 
271
- Create `tailwind-to-style.config.js` in your project root:
427
+ | Function | Description |
428
+ |---|---|
429
+ | `configure(config)` | Apply custom configuration |
430
+ | `getConfig()` | Get current configuration |
431
+ | `resetConfig()` | Reset to defaults |
432
+ | `clearConfigCache()` | Clear cached config lookups |
433
+
434
+ ### Plugin System
435
+
436
+ Create reusable plugins to extend the utility set:
272
437
 
273
438
  ```javascript
274
- export default {
275
- theme: {
276
- extend: {
277
- colors: {
278
- primary: '#3b82f6',
279
- secondary: '#10b981'
280
- }
281
- }
282
- }
283
- }
284
- ```
439
+ import { configure, createPlugin, createUtilityPlugin } from 'tailwind-to-style'
440
+
441
+ // Simple plugin — static utilities
442
+ const textShadow = createPlugin('text-shadow', {
443
+ utilities: {
444
+ 'text-shadow-sm': { textShadow: '0 1px 2px rgba(0,0,0,0.05)' },
445
+ 'text-shadow-md': { textShadow: '0 2px 4px rgba(0,0,0,0.1)' },
446
+ 'text-shadow-lg': { textShadow: '0 4px 8px rgba(0,0,0,0.15)' },
447
+ },
448
+ })
285
449
 
286
- ## 🎨 Preflight CSS (Base Styles)
450
+ // Dynamic plugin value-based utilities
451
+ const glass = createUtilityPlugin('glass', {
452
+ prefix: 'glass',
453
+ values: { sm: '4px', md: '8px', lg: '16px' },
454
+ formatter: (value) => ({
455
+ backdropFilter: `blur(${value})`,
456
+ backgroundColor: 'rgba(255,255,255,0.1)',
457
+ }),
458
+ })
287
459
 
288
- For best results, import Tailwind's preflight (base styles):
460
+ configure({ plugins: [textShadow, glass] })
289
461
 
290
- ```javascript
291
- // In your main entry file
292
- import 'tailwind-to-style/preflight.css'
462
+ // Now use custom utilities
463
+ tws('text-shadow-md glass-lg')
293
464
  ```
294
465
 
295
- ```html
296
- <!-- Or in HTML -->
297
- <link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">
298
- ```
466
+ ---
299
467
 
300
- The preflight CSS provides:
301
- - Consistent box-sizing
302
- - Reset margins and paddings
303
- - Normalized form elements
304
- - Better default font rendering
468
+ ## SSR (Server-Side Rendering)
305
469
 
306
- **Note:** Skip this if you're already using Tailwind CSS in your project.
470
+ Collect CSS during server-side rendering instead of injecting into the DOM:
307
471
 
308
- ## 💡 Use Cases
472
+ ```javascript
473
+ import { startSSR, stopSSR, getSSRStyles, twsx } from 'tailwind-to-style'
309
474
 
310
- ### 1. Dynamic Styling
475
+ // 1. Start collecting
476
+ startSSR()
311
477
 
312
- Generate styles from user input or runtime data:
478
+ // 2. Render your app (twsx() collects CSS instead of injecting)
479
+ twsx({ '.card': 'bg-white p-6 rounded-lg shadow-md' })
480
+ twsx({ '.btn': 'bg-blue-500 text-white px-4 py-2 rounded' })
481
+ const html = renderToString(<App />)
313
482
 
314
- ```javascript
315
- import { tws } from 'tailwind-to-style'
483
+ // 3. Get collected CSS
484
+ const css = stopSSR()
316
485
 
317
- function UserCard({ user }) {
318
- const styles = tws(`bg-${user.color}-500 p-4 rounded-lg`)
319
- return <div style={styles}>{user.name}</div>
320
- }
486
+ // 4. Inject into HTML response
487
+ const fullHtml = `
488
+ <html>
489
+ <head><style>${css}</style></head>
490
+ <body>${html}</body>
491
+ </html>
492
+ `
321
493
  ```
322
494
 
323
- ### 2. Email Templates
495
+ **SSR API:**
324
496
 
325
- Generate inline styles for email HTML:
497
+ | Function | Description |
498
+ |---|---|
499
+ | `startSSR()` | Begin collecting CSS |
500
+ | `stopSSR()` | Stop collecting, return all CSS as string |
501
+ | `getSSRStyles()` | Peek at collected CSS without stopping |
502
+ | `IS_BROWSER` | `true` in browser environment |
503
+ | `IS_SERVER` | `true` in Node.js/server environment |
326
504
 
327
- ```javascript
328
- import { tws } from 'tailwind-to-style'
505
+ ---
329
506
 
330
- const emailHTML = `
331
- <div style="${tws('bg-white p-8 text-center', { format: 'css' })}">
332
- <h1 style="${tws('text-2xl font-bold mb-4', { format: 'css' })}">
333
- Welcome!
334
- </h1>
335
- </div>
336
- `
337
- ```
507
+ ## Animation System
338
508
 
339
- ### 3. CSS-in-JS Alternative
509
+ ### Built-in CSS Animations
340
510
 
341
511
  ```javascript
342
- import { twsx } from 'tailwind-to-style'
512
+ tws('animate-spin') // animation: spin 1s linear infinite
513
+ tws('animate-bounce') // → animation: bounce 1s infinite
514
+ tws('animate-pulse') // → animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
515
+ tws('animate-ping') // → animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
516
+ ```
343
517
 
344
- const styles = twsx({
345
- '.app': [
346
- 'min-h-screen bg-gray-50',
347
- {
348
- '> .header': 'bg-white shadow-sm p-4',
349
- '> .main': 'container mx-auto py-8',
350
- '> .footer': 'bg-gray-900 text-white p-8'
351
- }
352
- ]
353
- })
518
+ ### Web Animations API
519
+
520
+ ```javascript
521
+ import { applyWebAnimation } from 'tailwind-to-style'
354
522
 
355
- // Inject once at app startup
356
- document.head.appendChild(Object.assign(document.createElement('style'), {
357
- textContent: styles
358
- }))
523
+ // Apply a named animation to a DOM element
524
+ applyWebAnimation(element, 'fadeIn')
525
+ applyWebAnimation(element, 'slideUp')
359
526
  ```
360
527
 
361
- ### 4. Component Library Styling
528
+ ### Inline Animations
362
529
 
363
530
  ```javascript
364
- import { twsx } from 'tailwind-to-style'
531
+ import { applyInlineAnimation, animateElement, chainAnimations, staggerAnimations } from 'tailwind-to-style'
365
532
 
366
- export const buttonStyles = twsx({
367
- '.btn': [
368
- 'px-4 py-2 rounded-lg font-medium transition-colors',
369
- {
370
- '&.btn-primary': 'bg-blue-500 text-white hover:bg-blue-600',
371
- '&.btn-secondary': 'bg-gray-200 text-gray-900 hover:bg-gray-300',
372
- '&.btn-lg': 'px-6 py-3 text-lg',
373
- '&.btn-sm': 'px-2 py-1 text-sm'
374
- }
375
- ]
376
- })
533
+ // Single animation
534
+ applyInlineAnimation(element, 'fadeIn')
535
+
536
+ // Programmatic animation
537
+ animateElement(element, { opacity: [0, 1] }, { duration: 300 })
538
+
539
+ // Sequential chain
540
+ chainAnimations(element, ['fadeIn', 'slideUp', 'bounceIn'])
541
+
542
+ // Staggered across multiple elements
543
+ staggerAnimations('.card', 'fadeIn', { delay: 100 })
377
544
  ```
378
545
 
379
- ## 🔧 Advanced Features
546
+ ---
380
547
 
381
- ### Responsive Design
548
+ ## Tree-Shakeable Imports
382
549
 
383
- All Tailwind breakpoints work out of the box:
550
+ Import only what you need to reduce bundle size by **50-70%**:
384
551
 
385
552
  ```javascript
386
- const styles = tws('text-sm sm:text-base md:text-lg lg:text-xl xl:text-2xl')
553
+ // Individual imports (recommended for production)
554
+ import { tws } from 'tailwind-to-style/tws' // ~3KB
555
+ import { twsx } from 'tailwind-to-style/twsx' // ~6KB
556
+ import { twsxVariants } from 'tailwind-to-style/twsx-variants' // ~6KB
557
+ import { cx } from 'tailwind-to-style/cx' // <1KB
558
+
559
+ // Full import (everything)
560
+ import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style' // ~12KB
387
561
  ```
388
562
 
389
- ### Arbitrary Values
563
+ | Import Path | Includes | Size (minified) |
564
+ |---|---|---|
565
+ | `tailwind-to-style` | Everything | ~12KB |
566
+ | `tailwind-to-style/tws` | `tws()` only | ~3KB |
567
+ | `tailwind-to-style/twsx` | `twsx()` | ~6KB |
568
+ | `tailwind-to-style/twsx-variants` | `twsxVariants()` | ~6KB |
569
+ | `tailwind-to-style/cx` | `cx()` | <1KB |
570
+ | `tailwind-to-style/utils` | Logger, LRUCache, error handler | ~2KB |
390
571
 
391
- Use any custom value with square brackets:
572
+ All sub-paths provide ESM + CJS bundles with TypeScript type definitions.
392
573
 
393
- ```javascript
394
- const styles = tws(`
395
- w-[123px]
396
- h-[calc(100vh-64px)]
397
- text-[#abc123]
398
- bg-[url('/image.png')]
399
- grid-cols-[1fr,2fr,1fr]
400
- `)
401
- ```
574
+ ---
575
+
576
+ ## Preflight CSS
402
577
 
403
- ### State Variants
578
+ For best results, import Tailwind's preflight (base/reset styles):
404
579
 
405
580
  ```javascript
406
- const styles = tws(`
407
- bg-blue-500
408
- hover:bg-blue-600
409
- focus:ring-2
410
- active:scale-95
411
- disabled:opacity-50
412
- group-hover:text-white
413
- `)
581
+ import 'tailwind-to-style/preflight.css'
414
582
  ```
415
583
 
416
- ### Important Modifier
417
-
418
- ```javascript
419
- const styles = tws('!bg-red-500 !text-white')
420
- // Forces these styles to take precedence
584
+ ```html
585
+ <!-- Or in HTML -->
586
+ <link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">
421
587
  ```
422
588
 
423
- ## 🎭 Framework Integration
589
+ Provides consistent box-sizing, reset margins/paddings, normalized form elements, and better default font rendering. Skip this if you're already using Tailwind CSS in your project.
590
+
591
+ ---
592
+
593
+ ## Framework Integration
424
594
 
425
595
  ### React
426
596
 
427
- ```javascript
597
+ ```jsx
428
598
  import { tws, twsx } from 'tailwind-to-style'
429
599
  import { useEffect } from 'react'
430
600
 
431
601
  function App() {
602
+ // Inject CSS on mount
432
603
  useEffect(() => {
433
- const css = twsx({
434
- '.custom': 'bg-blue-500 text-white p-4'
604
+ twsx({
605
+ '.card': ['bg-white rounded-xl shadow-md p-6', {
606
+ '&:hover': 'shadow-xl',
607
+ '> .title': 'text-xl font-bold',
608
+ }]
435
609
  })
436
- const style = document.createElement('style')
437
- style.textContent = css
438
- document.head.appendChild(style)
439
610
  }, [])
440
611
 
441
612
  return (
442
- <div style={tws('flex items-center gap-4')}>
443
- <button style={tws('bg-blue-500 text-white px-4 py-2 rounded')}>
613
+ <div style={tws('flex items-center gap-4', true)}>
614
+ <button style={tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)}>
444
615
  Click me
445
616
  </button>
446
617
  </div>
@@ -453,72 +624,148 @@ function App() {
453
624
  ```vue
454
625
  <script setup>
455
626
  import { tws } from 'tailwind-to-style'
456
-
457
- const buttonStyle = tws('bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600')
627
+ const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600', true)
458
628
  </script>
459
629
 
460
630
  <template>
461
- <button :style="buttonStyle">Click me</button>
631
+ <button :style="btnStyle">Click me</button>
462
632
  </template>
463
633
  ```
464
634
 
635
+ ### Svelte
636
+
637
+ ```svelte
638
+ <script>
639
+ import { tws } from 'tailwind-to-style'
640
+ const style = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
641
+ </script>
642
+
643
+ <button style={Object.entries(style).map(([k,v]) => `${k}:${v}`).join(';')}>
644
+ Click me
645
+ </button>
646
+ ```
647
+
465
648
  ### Vanilla JS
466
649
 
467
650
  ```javascript
468
651
  import { tws, twsx } from 'tailwind-to-style'
469
652
 
470
- // Create element with styles
471
- const button = document.createElement('button')
472
- Object.assign(button.style, tws('bg-blue-500 text-white px-4 py-2 rounded'))
473
- button.textContent = 'Click me'
653
+ // Inline styles
654
+ const el = document.createElement('button')
655
+ Object.assign(el.style, tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true))
474
656
 
475
657
  // Inject global styles
476
- const css = twsx({
477
- '.card': 'bg-white p-6 rounded-lg shadow-md'
658
+ twsx({
659
+ '.card': 'bg-white p-6 rounded-lg shadow-md',
660
+ '.card:hover': 'shadow-xl',
478
661
  })
479
- document.head.appendChild(Object.assign(document.createElement('style'), {
480
- textContent: css
481
- }))
482
662
  ```
483
663
 
484
- ## 📊 Performance
664
+ ---
665
+
666
+ ## Performance
667
+
668
+ v3.2.0 includes major performance optimizations:
669
+
670
+ - **Pre-compiled regex** — compiled once at module load, reused for every call
671
+ - **Multi-level LRU caching** — class resolution, CSS generation, config lookups
672
+ - **Bounded caches** — Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
673
+ - **`sheet.insertRule()` injection** — avoids full stylesheet reparsing on each call
674
+ - **FNV-1a hashing** — 100x faster than `JSON.stringify` for cache keys
675
+
676
+ ```
677
+ Parse 10,000 classes:
678
+ Cold: ~12ms
679
+ Cached: ~0.12ms (100x faster)
680
+
681
+ Bundle sizes:
682
+ Full import: ~12KB minified
683
+ tws() only: ~3KB minified
684
+ twsx() only: ~6KB minified
685
+ ```
686
+
687
+ **Performance utilities:**
688
+
689
+ ```javascript
690
+ import { performanceUtils } from 'tailwind-to-style'
691
+
692
+ // View cache stats
693
+ performanceUtils.getStats()
694
+
695
+ // Clear all caches
696
+ performanceUtils.clearCaches()
697
+
698
+ // Enable performance logging
699
+ performanceUtils.enablePerformanceLogging(true)
700
+ ```
701
+
702
+ ---
485
703
 
486
- - **Parse & cache** - Styles are cached after first parse
487
- - **Small bundle** - ~12KB minified (vs 45KB in v2)
488
- - **No build step** - Instant development workflow
489
- - **Tree-shakeable** - Only import what you use
704
+ ## Debugging & Logging
490
705
 
491
- ## 🆚 Comparison
706
+ Logging is disabled by default. Enable via environment variable or programmatically:
492
707
 
493
- | Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS |
494
- |---------|------------------|--------------|-----------|
495
- | Build Step | ❌ None | ✅ Required | ❌ None |
496
- | Bundle Size | 🟢 12KB | 🟡 ~80KB+ | 🟡 20-40KB |
497
- | Runtime | ✅ Yes | ❌ No | ✅ Yes |
498
- | Full Tailwind Support | ✅ Yes | ✅ Yes | ❌ No |
499
- | Framework Agnostic | ✅ Yes | ✅ Yes | ⚠️ Depends |
500
- | Nesting Support | ✅ Yes | ⚠️ Plugins | ✅ Yes |
501
- | TypeScript | ✅ Yes | ✅ Yes | ✅ Yes |
708
+ ```bash
709
+ TWSX_LOG_LEVEL=debug npm start # debug, info, warn, error, silent
710
+ ```
502
711
 
503
- ## 📖 Migration from v2
712
+ ```javascript
713
+ import { logger } from 'tailwind-to-style'
504
714
 
505
- See [MIGRATION.md](MIGRATION.md) for detailed migration guide from v2.x to v3.x.
715
+ logger.setLevel('debug')
716
+ console.log(logger.getLevel()) // → 'debug'
717
+ ```
718
+
719
+ | Level | Description |
720
+ |---|---|
721
+ | `debug` | Detailed processing info |
722
+ | `info` | General information |
723
+ | `warn` | Performance warnings |
724
+ | `error` | Errors only |
725
+ | `silent` | No logging (default) |
726
+
727
+ ---
728
+
729
+ ## Comparison
730
+
731
+ | Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS | tailwind-variants |
732
+ |---|:---:|:---:|:---:|:---:|
733
+ | Build Step | None | Required | None | Required |
734
+ | Bundle Size | 3-12KB | ~80KB+ | 20-40KB | ~15KB |
735
+ | Runtime Styles | Yes | No | Yes | Partial |
736
+ | Full Tailwind Support | Yes | Yes | No | Classes only |
737
+ | SSR Support | Yes | Yes | Depends | Yes |
738
+ | Variant System | Built-in | No | No | Yes |
739
+ | Conditional Classes | `cx()` | No | No | `tv()` |
740
+ | SCSS-like Nesting | Yes | Plugins | Yes | No |
741
+ | @css Raw Injection | Yes | No | Yes | No |
742
+ | Framework Agnostic | Yes | Yes | Depends | Yes |
743
+ | Tree-Shaking | Yes | Partial | Yes | Yes |
744
+ | TypeScript Generics | Yes | Yes | Yes | Yes |
745
+ | Zero Dependencies | Yes | PostCSS | No | tailwind-merge |
746
+
747
+ ---
748
+
749
+ ## Migration from v2
750
+
751
+ See [MIGRATION.md](MIGRATION.md) for the detailed guide from v2.x to v3.x.
506
752
 
507
753
  **Quick summary:**
508
- - ✅ `tws()` - No changes
509
- - ✅ `twsx()` - No changes
510
- - ✅ `configure()` - No changes
511
- - ❌ `styled()`, `tv()` - Removed (use emotion/styled-components)
512
- - ❌ `useTwsx()`, `TwsxProvider` - Removed (use `twsx()` directly)
513
- - ❌ CLI tools & plugins - Removed (not needed for runtime library)
514
754
 
515
- ## 🤝 Contributing
755
+ | Status | API |
756
+ |---|---|
757
+ | No changes | `tws()`, `twsx()`, `configure()` |
758
+ | New in v3.1 | `twsxVariants()` |
759
+ | New in v3.2 | `cx()`, SSR, tree-shakeable imports |
760
+ | Removed | `styled()`, `tv()`, `useTwsx()`, `TwsxProvider`, CLI tools |
516
761
 
517
- Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details.
762
+ ---
518
763
 
519
- ## 📄 License
764
+ ## Contributing
520
765
 
521
- MIT © [Bigetion](https://github.com/Bigetion)
766
+ Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for architecture overview, testing guidelines, and build output docs.
767
+
768
+ ---
522
769
 
523
770
  ## 💖 Support
524
771
 
@@ -528,4 +775,10 @@ If you find this library helpful, consider supporting:
528
775
 
529
776
  ---
530
777
 
531
- **v3.0.0** - Focused, fast, and simple. Just the core. 🎯
778
+ ## License
779
+
780
+ MIT © [Bigetion](https://github.com/Bigetion)
781
+
782
+ ---
783
+
784
+ **v3.2.0** — [Changelog](CHANGELOG.md) · [Architecture](ARCHITECTURE.md) · [Migration Guide](MIGRATION.md)