tailwind-to-style 3.3.0 โ†’ 4.0.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.
Files changed (52) hide show
  1. package/README.md +222 -1109
  2. package/dist/animations/index.cjs +9391 -0
  3. package/dist/animations/index.d.ts +58 -0
  4. package/dist/animations/index.esm.js +9385 -0
  5. package/dist/animations/index.esm.js.map +1 -0
  6. package/dist/className/index.cjs +2241 -4181
  7. package/dist/className/index.esm.js +2241 -4181
  8. package/dist/className/index.esm.js.map +1 -1
  9. package/dist/core/tws.cjs +136 -114
  10. package/dist/core/tws.cjs.map +1 -0
  11. package/dist/core/tws.esm.js +136 -114
  12. package/dist/core/tws.esm.js.map +1 -1
  13. package/dist/core/twsx.cjs +1971 -3970
  14. package/dist/core/twsx.esm.js +1971 -3970
  15. package/dist/core/twsx.esm.js.map +1 -1
  16. package/dist/core/twsxVariants.cjs +1997 -3986
  17. package/dist/core/twsxVariants.esm.js +1997 -3986
  18. package/dist/core/twsxVariants.esm.js.map +1 -1
  19. package/dist/cx.cjs +2 -2
  20. package/dist/cx.cjs.map +1 -0
  21. package/dist/cx.esm.js +2 -2
  22. package/dist/index.cjs +5253 -9252
  23. package/dist/index.cjs.map +1 -0
  24. package/dist/index.d.ts +4 -1
  25. package/dist/index.esm.js +5251 -9201
  26. package/dist/index.esm.js.map +1 -1
  27. package/dist/index.min.js +1 -1
  28. package/dist/index.min.js.map +1 -1
  29. package/dist/react/index.cjs +10177 -0
  30. package/dist/react/index.cjs.map +1 -0
  31. package/dist/react/index.d.ts +69 -0
  32. package/dist/react/index.esm.js +10173 -0
  33. package/dist/react/index.esm.js.map +1 -0
  34. package/dist/styled/index.cjs +9094 -0
  35. package/dist/styled/index.cjs.map +1 -0
  36. package/dist/styled/index.d.ts +17 -0
  37. package/dist/styled/index.esm.js +9087 -0
  38. package/dist/styled/index.esm.js.map +1 -0
  39. package/dist/tokens/index.cjs +359 -0
  40. package/dist/tokens/index.d.ts +33 -0
  41. package/dist/tokens/index.esm.js +355 -0
  42. package/dist/tokens/index.esm.js.map +1 -0
  43. package/dist/utils/index.cjs +219 -270
  44. package/dist/utils/index.esm.js +219 -270
  45. package/dist/utils/index.esm.js.map +1 -1
  46. package/llms.txt +481 -0
  47. package/package.json +38 -24
  48. package/types/animations/index.d.ts +58 -0
  49. package/types/index.d.ts +4 -1
  50. package/types/react/index.d.ts +69 -0
  51. package/types/tokens/index.d.ts +33 -0
  52. package/types/v4.d.ts +191 -0
package/README.md CHANGED
@@ -1,69 +1,27 @@
1
1
  # tailwind-to-style
2
2
 
3
- [๐Ÿ“ฆ View on npm](https://www.npmjs.com/package/tailwind-to-style)
4
- | [๐ŸŒ Landing Page](https://bigetion.github.io/tailwind-to-style/landing.html)
5
- | [๐Ÿ› Playground](https://bigetion.github.io/tailwind-to-style/sandbox.html)
6
-
7
3
  [![npm version](https://img.shields.io/npm/v/tailwind-to-style.svg)](https://www.npmjs.com/package/tailwind-to-style)
8
- [![Build Status](https://github.com/Bigetion/tailwind-to-style/workflows/CI%2FCD/badge.svg)](https://github.com/Bigetion/tailwind-to-style/actions)
9
- [![npm downloads](https://img.shields.io/npm/dm/tailwind-to-style.svg)](https://www.npmjs.com/package/tailwind-to-style)
10
4
  [![bundle size](https://img.shields.io/bundlephobia/minzip/tailwind-to-style)](https://bundlephobia.com/package/tailwind-to-style)
11
5
  [![license](https://img.shields.io/npm/l/tailwind-to-style.svg)](https://github.com/Bigetion/tailwind-to-style/blob/main/LICENSE)
12
6
 
13
- > **Runtime Tailwind CSS to inline styles converter.**
14
- > Zero build step. SSR-ready. Tree-shakeable. Works everywhere โ€” React, Vue, Svelte, Node.js, vanilla JS.
15
-
16
- ---
17
-
18
- ## Table of Contents
19
-
20
- - [Why tailwind-to-style?](#why-tailwind-to-style)
21
- - [Installation](#installation)
22
- - [Quick Start](#quick-start)
23
- - [Core API](#core-api)
24
- - [`tws()` โ€” Tailwind to Inline Styles](#tws--tailwind-to-inline-styles)
25
- - [`twsx()` โ€” CSS-in-JS Engine](#twsx--css-in-js-engine)
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)
29
- - [`cx()` โ€” Conditional Class Builder](#cx--conditional-class-builder)
30
- - [Configuration & Plugins](#configuration--plugins)
31
- - [`configure()` โ€” Custom Theme](#configure--custom-theme)
32
- - [Plugin System](#plugin-system)
33
- - [SSR (Server-Side Rendering)](#ssr-server-side-rendering)
34
- - [Animation System](#animation-system)
35
- - [Tree-Shakeable Imports](#tree-shakeable-imports)
36
- - [Preflight CSS](#preflight-css)
37
- - [Framework Integration](#framework-integration)
38
- - [Performance](#performance)
39
- - [Debugging & Logging](#debugging--logging)
40
- - [Comparison](#comparison)
41
- - [Migration from v2](#migration-from-v2)
42
- - [Contributing](#contributing)
43
- - [Support](#-support)
44
- - [License](#license)
7
+ **Zero-build runtime Tailwind CSS engine.** Convert utility classes to real CSS โ€” with variants, slots, design tokens, and React bindings. No build step, no PostCSS, no config file. Just works.
45
8
 
46
9
  ---
47
10
 
48
11
  ## Why tailwind-to-style?
49
12
 
50
- | Feature | Description |
51
- |---|---|
52
- | **Zero Build Step** | No PostCSS, no compilation โ€” just JavaScript |
53
- | **Framework Agnostic** | React, Vue, Svelte, vanilla JS |
54
- | **Full Tailwind Support** | All utilities, responsive, pseudo-states, arbitrary values |
55
- | **SCSS-like Nesting** | `twsx()` for complex nested selector-based styles |
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 |
59
- | **Conditional Classes** | Built-in `cx()` utility (like clsx/classnames) |
60
- | **SSR Support** | Server-side rendering with `startSSR()`/`stopSSR()` |
61
- | **@css Directive** | Inject raw CSS for vendor-specific or complex properties |
62
- | **Customizable** | Extend theme with colors, spacing, fonts, plugins |
63
- | **TypeScript** | Full type definitions with generics for autocomplete |
64
- | **Tree-Shakeable** | Import only what you need โ€” reduce bundle by 50-70% |
65
- | **Lightweight** | ~12KB minified, zero runtime dependencies |
66
- | **Lightning Fast** | Pre-compiled regex + multi-level LRU caching |
13
+ | Feature | tailwind-to-style | Tailwind CSS | Stitches | CVA |
14
+ |---------|:---:|:---:|:---:|:---:|
15
+ | Zero build step | โœ… | โŒ | โœ… | โŒ |
16
+ | Tailwind syntax | โœ… | โœ… | โŒ | โœ… |
17
+ | Runtime variants | โœ… | โŒ | โœ… | โœ… |
18
+ | Slots (multi-part) | โœ… | โŒ | โŒ | โŒ |
19
+ | Design tokens | โœ… | โŒ | โœ… | โŒ |
20
+ | Inline style output | โœ… | โŒ | โŒ | โŒ |
21
+ | SSR support | โœ… | โœ… | โœ… | โœ… |
22
+ | React bindings | โœ… | โŒ | โœ… | โŒ |
23
+ | Framework agnostic | โœ… | โœ… | โŒ | โœ… |
24
+ | Tree-shakeable | โœ… | N/A | โœ… | โœ… |
67
25
 
68
26
  ---
69
27
 
@@ -74,1211 +32,366 @@ npm install tailwind-to-style
74
32
  ```
75
33
 
76
34
  ```bash
77
- yarn add tailwind-to-style
35
+ pnpm add tailwind-to-style
78
36
  ```
79
37
 
80
38
  ```bash
81
- pnpm add tailwind-to-style
39
+ yarn add tailwind-to-style
82
40
  ```
83
41
 
84
- **CDN (browser):**
42
+ Or use a CDN:
85
43
 
86
44
  ```html
87
45
  <script src="https://unpkg.com/tailwind-to-style"></script>
88
- <script>
89
- const { tws, twsx } = tailwindToStyle
90
- </script>
91
46
  ```
92
47
 
93
48
  ---
94
49
 
95
50
  ## Quick Start
96
51
 
97
- ```javascript
98
- import { tws, twsx, twsxVariants, cx } from 'tailwind-to-style'
99
-
100
- // 1. Inline styles
101
- const style = tws('bg-blue-500 text-white p-4 rounded-lg', true)
102
- // โ†’ { backgroundColor: '#3b82f6', color: '#fff', padding: '1rem', borderRadius: '0.5rem' }
103
-
104
- // 2. Real CSS with selectors
105
- twsx({
106
- '.card': ['bg-white p-6 rounded-xl shadow-md', {
107
- '&:hover': 'shadow-xl',
108
- '> .title': 'text-xl font-bold text-gray-900',
109
- }]
110
- })
111
- // โ†’ auto-injects <style> with .card { ... } .card:hover { ... }
112
-
113
- // 3. Component variants
114
- const btn = twsxVariants('.btn', {
115
- base: 'px-4 py-2 rounded-lg font-medium',
116
- variants: {
117
- color: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },
118
- size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
119
- },
120
- defaultVariants: { color: 'primary', size: 'md' },
121
- })
122
- btn({ color: 'danger', size: 'lg' }) // โ†’ "btn btn-danger-lg"
123
-
124
- // 4. Conditional classes
125
- cx('p-4', isActive && 'bg-blue-500', { 'opacity-50': isDisabled })
126
- // โ†’ 'p-4 bg-blue-500'
127
- ```
128
-
129
- ---
130
-
131
- ## Core API
52
+ ```js
53
+ import { tw, tws, cx } from 'tailwind-to-style';
132
54
 
133
- ### `tws()` โ€” Tailwind to Inline Styles
55
+ // Generate atomic CSS classes (auto-injected into DOM)
56
+ document.body.className = tw('flex items-center justify-center min-h-screen bg-gray-100');
134
57
 
135
- Converts Tailwind CSS class strings into CSS string or JSON style objects at runtime.
58
+ // Convert to inline styles
59
+ element.style.cssText = tws('bg-blue-500 text-white p-4 rounded-lg');
136
60
 
137
- ```javascript
138
- import { tws } from 'tailwind-to-style'
139
-
140
- // CSS string (default)
141
- tws('bg-blue-500 p-4 rounded-lg')
142
- // โ†’ "background-color: #3b82f6; padding: 1rem; border-radius: 0.5rem;"
143
-
144
- // JSON object (pass `true` as 2nd argument)
145
- tws('flex items-center gap-4', true)
146
- // โ†’ { display: 'flex', alignItems: 'center', gap: '1rem' }
61
+ // Conditional class merging
62
+ const classes = cx('base', isActive && 'ring-2', { 'opacity-50': disabled });
147
63
  ```
148
64
 
149
- **Supported features:**
150
-
151
- ```javascript
152
- // Responsive breakpoints
153
- tws('text-sm md:text-base lg:text-lg')
154
-
155
- // Pseudo-states
156
- tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
65
+ ---
157
66
 
158
- // Arbitrary values
159
- tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')
67
+ ## Examples
160
68
 
161
- // Important modifier
162
- tws('!bg-red-500 !text-white')
69
+ Want to try the library with real demos?
163
70
 
164
- // Negative values
165
- tws('-mt-4 -translate-x-2')
71
+ - `examples/basic/` โ€” runtime `tws()` examples for inline conversion and custom values.
72
+ - `examples/react-demo/` โ€” full React showcase with components, variants, tokens, and theme switching.
73
+ - `examples/twsx-classname-app/` โ€” Vite-based runtime `tw()` v4 demo with variant and slots components.
166
74
 
167
- // Opacity modifier
168
- tws('bg-blue-500/50 text-black/75')
75
+ Run the demos by opening `examples/README.md` or using the commands below:
169
76
 
170
- // Decimal spacing
171
- tws('p-0.5 m-1.5 gap-2.5')
77
+ ```bash
78
+ cd examples/react-demo
79
+ npm install
80
+ npm run dev
172
81
  ```
173
82
 
174
- **Use in React:**
175
-
176
- ```jsx
177
- <div style={tws('flex items-center gap-4 bg-white p-6 rounded-xl shadow-md', true)}>
178
- <h1 style={tws('text-2xl font-bold text-gray-900', true)}>Hello</h1>
179
- </div>
83
+ ```bash
84
+ cd examples/twsx-classname-app
85
+ npm install
86
+ npm run dev
180
87
  ```
181
88
 
182
89
  ---
183
90
 
184
- ### `twsx()` โ€” CSS-in-JS Engine
185
-
186
- Generates real CSS from Tailwind classes with full selector support, SCSS-like nesting, and auto-injects a `<style>` tag into the DOM.
187
-
188
- > **HMR-safe** โ€” each `twsx()` call owns a stable slot in the injected style tag keyed by its top-level selectors. When you edit styles during development, the old slot is **replaced** (not appended), so changes are reflected immediately without a hard refresh.
189
-
190
- ```javascript
191
- import { twsx } from 'tailwind-to-style'
192
-
193
- twsx({
194
- '.button': [
195
- 'bg-blue-500 text-white px-6 py-3 rounded-lg font-medium transition-all',
196
- {
197
- '&:hover': 'bg-blue-600 shadow-lg transform scale-105',
198
- '&:active': 'bg-blue-700 scale-95',
199
- '&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
200
- '&.large': 'px-8 py-4 text-lg',
201
- }
202
- ],
203
-
204
- '.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
205
- '.card > .header': 'p-6 border-b border-gray-200',
206
- '.card > .body': 'p-6',
207
-
208
- // Media queries
209
- '@media (max-width: 768px)': {
210
- '.card': 'rounded-lg',
211
- '.card > .header': 'p-4',
212
- }
213
- })
214
- ```
91
+ ## API Reference
215
92
 
216
- **Nesting syntax:**
93
+ ### `tw()` โ€” The Main Function
217
94
 
218
- | Pattern | Example | Description |
219
- |---|---|---|
220
- | `&:pseudo` | `'&:hover': 'bg-blue-600'` | Pseudo-classes |
221
- | `&.modifier` | `'&.active': 'ring-2'` | Class modifiers |
222
- | `> .child` | `'> .title': 'text-xl'` | Direct children |
223
- | `.descendant` | `'.icon': 'w-5 h-5'` | Descendants |
224
- | `@media` | `'@media (max-width: 768px)': { ... }` | Media queries |
95
+ One function, four modes:
225
96
 
226
- **Options:**
97
+ #### Mode 1: String โ†’ Atomic Classes
227
98
 
228
- ```javascript
229
- // Disable auto-injection (returns CSS string only)
230
- const css = twsx({ '.btn': 'bg-blue-500 text-white' }, { inject: false })
99
+ ```js
100
+ tw('flex items-center gap-4 hover:bg-gray-100')
101
+ // โ†’ "tw-flex tw-items-center tw-gap-4 tw-hover-bg-gray-100"
102
+ // CSS is auto-injected with full pseudo-class support
231
103
  ```
232
104
 
233
- #### `@css` Directive โ€” Raw CSS Escape Hatch
234
-
235
- For CSS that Tailwind can't express, use the `@css` directive:
236
-
237
- **String form:**
238
-
239
- ```javascript
240
- twsx({
241
- '.gradient-text': '@css { background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
242
- })
243
- ```
105
+ #### Mode 2: Named Class
244
106
 
245
- **Object form (within arrays):**
246
-
247
- ```javascript
248
- twsx({
249
- '.gradient-text': [
250
- 'text-3xl font-bold',
251
- {
252
- '@css': {
253
- 'background': 'linear-gradient(90deg, #ff6b6b, #feca57)',
254
- '-webkit-background-clip': 'text',
255
- '-webkit-text-fill-color': 'transparent',
256
- },
257
- },
258
- ],
259
- })
107
+ ```js
108
+ tw('sidebar', 'w-64 h-screen bg-white border-r border-gray-200')
109
+ // โ†’ "sidebar"
110
+ // Generates .sidebar { ... } with all the Tailwind styles
260
111
  ```
261
112
 
262
- ---
263
-
264
- ### `twsxVariants()` โ€” Component Variant System
265
-
266
- A CVA-like API for building type-safe component variants. Auto-generates CSS for all combinations and returns a class name builder function.
113
+ #### Mode 3: Variants
267
114
 
268
- ```javascript
269
- import { twsxVariants } from 'tailwind-to-style'
270
-
271
- const btn = twsxVariants('.btn', {
272
- base: 'px-4 py-2 rounded-lg font-medium transition-all border',
115
+ ```js
116
+ const button = tw({
117
+ name: 'btn',
118
+ base: 'px-4 py-2 rounded-lg font-medium transition-all',
273
119
  variants: {
274
- variant: {
275
- solid: 'shadow-sm',
276
- outline: 'bg-transparent border-2',
277
- ghost: 'bg-transparent border-transparent',
278
- },
279
120
  color: {
280
- primary: 'bg-blue-500 text-white border-blue-500',
281
- danger: 'bg-red-500 text-white border-red-500',
282
- neutral: 'bg-gray-100 text-gray-900 border-gray-300',
121
+ primary: 'bg-blue-600 text-white hover:bg-blue-700',
122
+ danger: 'bg-red-600 text-white hover:bg-red-700',
123
+ ghost: 'bg-transparent text-gray-700 hover:bg-gray-100',
283
124
  },
284
125
  size: {
285
- sm: 'px-3 py-1.5 text-sm',
286
- md: 'px-4 py-2 text-base',
287
- lg: 'px-6 py-3 text-lg',
288
- },
289
- disabled: {
290
- true: 'opacity-50 cursor-not-allowed pointer-events-none',
126
+ sm: 'text-sm px-3 py-1.5',
127
+ md: 'text-base px-4 py-2',
128
+ lg: 'text-lg px-6 py-3',
291
129
  },
292
130
  },
293
- compoundVariants: [
294
- { variant: 'outline', color: 'primary', class: 'bg-transparent text-blue-600 border-blue-500' },
295
- { variant: 'outline', color: 'danger', class: 'bg-transparent text-red-600 border-red-500' },
296
- ],
297
- defaultVariants: { variant: 'solid', color: 'primary', size: 'md' },
298
- })
131
+ defaultVariants: { color: 'primary', size: 'md' },
132
+ });
299
133
 
300
- // Usage โ€” returns class name string
301
- btn() // "btn"
302
- btn({ color: 'danger' }) // "btn btn-danger"
303
- btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
134
+ button({ color: 'danger', size: 'lg' })
135
+ // โ†’ "btn btn--color-danger btn--size-lg"
304
136
  ```
305
137
 
306
- **Nested selectors** โ€” style child elements:
138
+ #### Mode 4: Slots (Multi-Part Components)
307
139
 
308
- ```javascript
309
- const alert = twsxVariants('.alert', {
310
- base: 'p-4 rounded-lg border flex gap-3',
311
- variants: {
312
- status: {
313
- info: 'bg-blue-50 border-blue-200 text-blue-800',
314
- error: 'bg-red-50 border-red-200 text-red-800',
315
- },
140
+ ```js
141
+ const card = tw({
142
+ name: 'card',
143
+ slots: {
144
+ root: 'bg-white rounded-xl shadow-lg overflow-hidden',
145
+ header: 'px-6 py-4 border-b border-gray-100',
146
+ body: 'px-6 py-4',
147
+ footer: 'px-6 py-4 bg-gray-50',
316
148
  },
317
- defaultVariants: { status: 'info' },
318
- nested: {
319
- '.alert-icon': 'flex-shrink-0 mt-0.5',
320
- '.alert-content': 'flex-1',
321
- '.alert-dismiss': 'p-1 rounded hover:bg-black/10 cursor-pointer',
322
- }
323
- })
324
- // Generates: .alert .alert-icon { ... }, .alert .alert-content { ... }, etc.
325
- ```
149
+ });
326
150
 
327
- **Class naming convention:**
328
-
329
- | Call | Returns | Why |
330
- |---|---|---|
331
- | `btn()` | `"btn"` | All defaults |
332
- | `btn({ color: 'danger' })` | `"btn btn-danger"` | One non-default |
333
- | `btn({ variant: 'outline', color: 'danger', size: 'lg' })` | `"btn btn-outline-danger-lg"` | All non-defaults |
334
-
335
- **TypeScript โ€” full generics support:**
336
-
337
- ```typescript
338
- import { twsxVariants, type VariantProps } from 'tailwind-to-style'
151
+ card()
152
+ // โ†’ { root: "card__root", header: "card__header", body: "card__body", footer: "card__footer" }
153
+ ```
339
154
 
340
- const button = twsxVariants('.btn', {
341
- base: 'px-4 py-2 rounded',
342
- variants: {
343
- variant: { solid: 'bg-blue-500', outline: 'border-2' },
344
- size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
345
- },
346
- defaultVariants: { variant: 'solid', size: 'md' },
347
- })
155
+ #### Utility Methods
348
156
 
349
- type ButtonProps = VariantProps<typeof button>
350
- // โ†’ { variant?: 'solid' | 'outline', size?: 'sm' | 'md' | 'lg' }
157
+ ```js
158
+ tw.extractCSS() // Get all generated CSS as string (SSR)
159
+ tw.clearCache() // Clear internal caches
160
+ tw.config({ prefix: 'my', hash: false }) // Configure globally
351
161
  ```
352
162
 
353
163
  ---
354
164
 
355
- ### `cx()` โ€” Conditional Class Builder
356
-
357
- A built-in utility for conditionally joining class names โ€” replaces `clsx`/`classnames`:
165
+ ### `tws()` โ€” Inline Styles
358
166
 
359
- ```javascript
360
- import { cx } from 'tailwind-to-style'
167
+ Convert Tailwind classes directly to CSS styles. No DOM injection needed.
361
168
 
362
- // Strings
363
- cx('bg-blue-500', 'text-white')
364
- // โ†’ 'bg-blue-500 text-white'
365
-
366
- // Conditionals
367
- cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
368
- // โ†’ 'p-4 bg-blue-500'
169
+ ```js
170
+ // Returns CSS string
171
+ tws('bg-blue-500 p-4 rounded-lg')
172
+ // โ†’ "background-color: rgb(59,130,246); padding: 1rem; border-radius: 0.5rem;"
369
173
 
370
- // Object syntax
371
- cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled })
372
- // โ†’ 'p-4 bg-blue-500'
174
+ // Returns JSON object (for React style prop, etc.)
175
+ tws('flex items-center gap-4', true)
176
+ // โ†’ { display: 'flex', alignItems: 'center', gap: '1rem' }
177
+ ```
373
178
 
374
- // Arrays
375
- cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
376
- // โ†’ 'p-4 bg-white ring-2 ring-blue-500'
179
+ ---
377
180
 
378
- // Combined with tws()
379
- const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))
380
- ```
181
+ ### `cx()` โ€” Conditional Class Names
381
182
 
382
- **`cx.with()` โ€” Base class factory:**
183
+ A lightweight `clsx` alternative built-in.
383
184
 
384
- ```javascript
385
- const btnClass = cx.with('px-4 py-2 rounded font-medium transition-colors')
185
+ ```js
186
+ cx('base-class', isActive && 'active-class', { 'disabled': isDisabled })
187
+ // โ†’ "base-class active-class"
386
188
 
387
- btnClass('bg-blue-500 text-white')
388
- // โ†’ 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
189
+ // Arrays work too
190
+ cx(['p-4', 'bg-white'], condition && ['ring-2', 'ring-blue-500'])
389
191
 
390
- btnClass({ 'opacity-50': disabled })
391
- // โ†’ 'px-4 py-2 rounded font-medium transition-colors opacity-50'
192
+ // Create pre-filled cx
193
+ const btnClass = cx.with('px-4 py-2 rounded font-medium');
194
+ btnClass('bg-blue-500') // โ†’ "px-4 py-2 rounded font-medium bg-blue-500"
392
195
  ```
393
196
 
394
197
  ---
395
198
 
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.
199
+ ## React Bindings
399
200
 
400
- ```javascript
401
- import { twsxClassName, tw } from 'tailwind-to-style'
201
+ ```bash
202
+ import { styled, ThemeProvider, useTheme, useTws } from 'tailwind-to-style/react';
402
203
  ```
403
204
 
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
- ```
205
+ ### `styled()` โ€” Create Styled Components
420
206
 
421
- **Variants Mode** โ€” Component variants like CVA/tailwind-variants:
207
+ ```jsx
208
+ import { styled } from 'tailwind-to-style/react';
422
209
 
423
- ```javascript
424
- const button = twsxClassName({
210
+ const Button = styled('button', {
425
211
  name: 'btn',
426
- base: 'px-4 py-2 font-medium rounded-lg transition-colors',
212
+ base: 'px-4 py-2 rounded-lg font-medium transition-colors',
427
213
  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',
214
+ color: {
215
+ primary: 'bg-blue-600 text-white hover:bg-blue-700',
216
+ secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
432
217
  },
433
218
  size: {
434
219
  sm: 'text-sm px-3 py-1.5',
435
- md: 'text-base px-4 py-2',
436
220
  lg: 'text-lg px-6 py-3',
437
221
  },
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
222
  },
451
- })
223
+ defaultVariants: { color: 'primary', size: 'sm' },
224
+ });
452
225
 
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>
226
+ // Variant props are type-safe and stripped from DOM
227
+ <Button color="primary" size="lg" onClick={handleClick}>
228
+ Click Me
229
+ </Button>
457
230
  ```
458
231
 
459
- **Slots Mode** โ€” Multi-part components:
232
+ ### `ThemeProvider` & `useTheme`
460
233
 
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
- ```
234
+ ```jsx
235
+ import { ThemeProvider, useTheme } from 'tailwind-to-style/react';
489
236
 
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({
237
+ const theme = {
530
238
  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: {...}, ... }
239
+ radius: { sm: '0.25rem', md: '0.5rem' },
240
+ };
587
241
 
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()
242
+ function App() {
243
+ return (
244
+ <ThemeProvider theme={theme}>
245
+ <MyComponent />
246
+ </ThemeProvider>
247
+ );
248
+ }
614
249
 
615
- // Get cache statistics
616
- twsxClassName.getCacheStats()
617
- // โ†’ { classNameCacheSize: 42, cssCacheSize: 38, styleRegistrySize: 15 }
250
+ function MyComponent() {
251
+ const { theme, setTheme } = useTheme();
252
+ // Access tokens via CSS variables: var(--tws-colors-primary)
253
+ }
618
254
  ```
619
255
 
620
- ---
621
-
622
- ### `tw()` โ€” Atomic CSS Classes
256
+ ### `useTws()` โ€” Inline Style Hook
623
257
 
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.
258
+ ```jsx
259
+ import { useTws } from 'tailwind-to-style/react';
625
260
 
626
- ```javascript
627
- import { tw } from 'tailwind-to-style'
628
- // or: import { twsxClassName } from '...' โ†’ twsxClassName.tw(...)
261
+ function Box({ classes }) {
262
+ const style = useTws(classes); // memoized style object
263
+ return <div style={style}>Content</div>;
264
+ }
629
265
  ```
630
266
 
631
- **Basic Usage:**
267
+ ---
632
268
 
633
- ```javascript
634
- // Returns: "tw-flex tw-gap-3 tw-items-center"
635
- tw('flex gap-3 items-center')
269
+ ## Design Tokens
636
270
 
637
- // Usage
638
- <div class="${tw('flex gap-3 items-center')}">
639
- <span>Item 1</span>
640
- <span>Item 2</span>
641
- </div>
271
+ ```js
272
+ import { createTheme, tokenRegistry, token } from 'tailwind-to-style/tokens';
642
273
  ```
643
274
 
644
- **With Pseudo-classes:**
275
+ ### `createTheme()`
645
276
 
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>
277
+ ```js
278
+ createTheme({
279
+ colors: {
280
+ primary: '#3b82f6',
281
+ secondary: '#8b5cf6',
282
+ success: '#10b981',
283
+ },
284
+ spacing: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
285
+ radius: { sm: '0.25rem', md: '0.5rem', lg: '1rem' },
286
+ }, { selector: ':root' });
287
+ // Injects CSS variables on :root:
288
+ // --tws-colors-primary: #3b82f6;
289
+ // --tws-colors-secondary: #8b5cf6;
290
+ // ...
654
291
  ```
655
292
 
656
- **With Responsive Breakpoints:**
293
+ ### `tokenRegistry`
657
294
 
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>
295
+ ```js
296
+ tokenRegistry.get('colors.primary') // โ†’ '#3b82f6'
297
+ tokenRegistry.set('colors.primary', '#2563eb')
298
+ tokenRegistry.toCSS() // โ†’ full :root CSS string
299
+ tokenRegistry.subscribe((tokens) => { /* react to changes */ })
665
300
  ```
666
301
 
667
- **Combining with twsxClassName:**
302
+ ### `token()` โ€” CSS Variable Reference
668
303
 
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>
304
+ ```js
305
+ token('colors.primary') // โ†’ "var(--tws-colors-primary)"
306
+ token('colors.primary', '#000') // โ†’ "var(--tws-colors-primary, #000)"
681
307
  ```
682
308
 
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
309
  ---
692
310
 
693
- ## Configuration & Plugins
694
-
695
- ### `configure()` โ€” Custom Theme
696
-
697
- Extend the default Tailwind theme with custom colors, spacing, fonts, and more.
698
-
699
- ```javascript
700
- import { configure } from 'tailwind-to-style'
701
-
702
- configure({
703
- theme: {
704
- extend: {
705
- colors: {
706
- brand: {
707
- 50: '#eff6ff',
708
- 100: '#dbeafe',
709
- 500: '#3b82f6',
710
- 600: '#2563eb',
711
- 900: '#1e3a8a',
712
- },
713
- accent: '#f59e0b',
714
- },
715
- spacing: {
716
- '128': '32rem',
717
- '144': '36rem',
718
- },
719
- fontFamily: {
720
- sans: ['Inter', 'system-ui', 'sans-serif'],
721
- },
722
- },
723
- },
724
- })
311
+ ## Animations
725
312
 
726
- // Now use custom values
727
- tws('bg-brand-500 text-brand-50 p-128 font-sans')
313
+ ```js
314
+ import { animate, defineAnimation, getAnimationNames } from 'tailwind-to-style/animations';
728
315
  ```
729
316
 
730
- **Config API:**
317
+ ### Built-in Presets
731
318
 
732
- | Function | Description |
733
- |---|---|
734
- | `configure(config)` | Apply custom configuration |
735
- | `getConfig()` | Get current configuration |
736
- | `resetConfig()` | Reset to defaults |
737
- | `clearConfigCache()` | Clear cached config lookups |
319
+ ```js
320
+ element.className = animate('fadeIn');
321
+ element.className = animate('slideInUp', { duration: '500ms', delay: '100ms' });
322
+ element.className = animate('bounce');
323
+ element.className = animate('spin'); // infinite
324
+ ```
738
325
 
739
- ### Plugin System
326
+ Available presets: `fadeIn`, `fadeOut`, `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `scaleIn`, `scaleOut`, `bounce`, `shake`, `pulse`, `spin`, `ping`
740
327
 
741
- Create reusable plugins to extend the utility set:
328
+ ### Custom Animations
742
329
 
743
- ```javascript
744
- import { configure, createPlugin, createUtilityPlugin } from 'tailwind-to-style'
330
+ ```js
331
+ defineAnimation('wiggle', {
332
+ keyframes: [
333
+ { transform: 'rotate(0deg)' },
334
+ { transform: 'rotate(-3deg)' },
335
+ { transform: 'rotate(3deg)' },
336
+ { transform: 'rotate(0deg)' },
337
+ ],
338
+ duration: '300ms',
339
+ easing: 'ease-in-out',
340
+ });
745
341
 
746
- // Simple plugin โ€” static utilities
747
- const textShadow = createPlugin('text-shadow', {
748
- utilities: {
749
- 'text-shadow-sm': { textShadow: '0 1px 2px rgba(0,0,0,0.05)' },
750
- 'text-shadow-md': { textShadow: '0 2px 4px rgba(0,0,0,0.1)' },
751
- 'text-shadow-lg': { textShadow: '0 4px 8px rgba(0,0,0,0.15)' },
752
- },
753
- })
754
-
755
- // Dynamic plugin โ€” value-based utilities
756
- const glass = createUtilityPlugin('glass', {
757
- prefix: 'glass',
758
- values: { sm: '4px', md: '8px', lg: '16px' },
759
- formatter: (value) => ({
760
- backdropFilter: `blur(${value})`,
761
- backgroundColor: 'rgba(255,255,255,0.1)',
762
- }),
763
- })
764
-
765
- configure({ plugins: [textShadow, glass] })
766
-
767
- // Now use custom utilities
768
- tws('text-shadow-md glass-lg')
342
+ animate('wiggle'); // works!
769
343
  ```
770
344
 
771
345
  ---
772
346
 
773
347
  ## SSR (Server-Side Rendering)
774
348
 
775
- Collect CSS during server-side rendering instead of injecting into the DOM:
349
+ ```js
350
+ import { tw, createSSRCollector } from 'tailwind-to-style';
776
351
 
777
- ```javascript
778
- import { startSSR, stopSSR, getSSRStyles, twsx } from 'tailwind-to-style'
352
+ // Collect all CSS generated during render
353
+ const collector = createSSRCollector();
779
354
 
780
- // 1. Start collecting
781
- startSSR()
355
+ const html = renderToString(<App />);
356
+ const css = collector.extract();
782
357
 
783
- // 2. Render your app (twsx() collects CSS instead of injecting)
784
- twsx({ '.card': 'bg-white p-6 rounded-lg shadow-md' })
785
- twsx({ '.btn': 'bg-blue-500 text-white px-4 py-2 rounded' })
786
- const html = renderToString(<App />)
787
-
788
- // 3. Get collected CSS
789
- const css = stopSSR()
790
-
791
- // 4. Inject into HTML response
358
+ // Inject into HTML head
792
359
  const fullHtml = `
793
360
  <html>
794
361
  <head><style>${css}</style></head>
795
362
  <body>${html}</body>
796
363
  </html>
797
- `
798
- ```
799
-
800
- **SSR API:**
801
-
802
- | Function | Description |
803
- |---|---|
804
- | `startSSR()` | Begin collecting CSS |
805
- | `stopSSR()` | Stop collecting, return all CSS as string |
806
- | `getSSRStyles()` | Peek at collected CSS without stopping |
807
- | `IS_BROWSER` | `true` in browser environment |
808
- | `IS_SERVER` | `true` in Node.js/server environment |
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
-
845
- ---
846
-
847
- ## Animation System
848
-
849
- ### Built-in CSS Animations
850
-
851
- Tailwind animation utilities work out of the box:
852
-
853
- ```javascript
854
- tws('animate-spin') // โ†’ animation: spin 1s linear infinite
855
- tws('animate-bounce') // โ†’ animation: bounce 1s infinite
856
- tws('animate-pulse') // โ†’ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
857
- tws('animate-ping') // โ†’ animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
858
- ```
859
-
860
- ### Programmatic Animations (Web Animations API)
861
-
862
- For interactive animations with full control:
863
-
864
- ```javascript
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')
894
-
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
- )
901
- ```
902
-
903
- ### Animation Presets
904
-
905
- ```javascript
906
- import { ANIMATION_PRESETS, EASING } from 'tailwind-to-style'
907
-
908
- // Available presets
909
- // fadeIn, fadeOut, slideUp, slideDown, slideLeft, slideRight,
910
- // zoomIn, zoomOut, bounce, shake, pulse, spin, flipX, flipY,
911
- // enterScale, exitScale, wiggle, heartbeat
912
-
913
- // Easing presets
914
- // linear, ease, easeIn, easeOut, easeInOut,
915
- // spring, springLight, springMedium, springHeavy,
916
- // smooth, smoothIn, smoothOut, bounce, elastic
917
- ```
918
-
919
- ### Custom Keyframes
920
-
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
364
+ `;
958
365
  ```
959
366
 
960
367
  ---
961
368
 
962
369
  ## Tree-Shakeable Imports
963
370
 
964
- Import only what you need to reduce bundle size by **50-70%**:
965
-
966
- ```javascript
967
- // Individual imports (recommended for production)
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
973
-
974
- // Full import (everything)
975
- import { tws, twsx, twsxVariants, twsxClassName, cx } from 'tailwind-to-style' // ~15KB
976
- ```
371
+ Import only what you need for minimal bundle size:
977
372
 
978
- | Import Path | Includes | Size (minified) |
373
+ | Import Path | What You Get | ~Size |
979
374
  |---|---|---|
980
- | `tailwind-to-style` | Everything | ~15KB |
981
- | `tailwind-to-style/tws` | `tws()` only | ~3KB |
982
- | `tailwind-to-style/twsx` | `twsx()` | ~6KB |
983
- | `tailwind-to-style/twsx-variants` | `twsxVariants()` | ~6KB |
984
- | `tailwind-to-style/classname` | `twsxClassName()`, `tw()` | ~8KB |
985
- | `tailwind-to-style/cx` | `cx()` | <1KB |
986
- | `tailwind-to-style/utils` | Logger, LRUCache, error handler | ~2KB |
987
-
988
- All sub-paths provide ESM + CJS bundles with TypeScript type definitions.
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
- ```
375
+ | `tailwind-to-style` | `tw`, `tws`, `cx` | Full engine |
376
+ | `tailwind-to-style/react` | `styled`, `ThemeProvider`, `useTheme`, `useTws` | +2KB |
377
+ | `tailwind-to-style/tokens` | `createTheme`, `tokenRegistry`, `token` | +1KB |
378
+ | `tailwind-to-style/animations` | `animate`, `defineAnimation` | +1.5KB |
379
+ | `tailwind-to-style/cx` | `cx` only | ~300B |
380
+ | `tailwind-to-style/tws` | `tws` only | Subset |
1042
381
 
1043
382
  ---
1044
383
 
1045
- ## Preflight CSS
1046
-
1047
- For best results, import Tailwind's preflight (base/reset styles):
384
+ ## Framework Support
1048
385
 
1049
- ```javascript
1050
- import 'tailwind-to-style/preflight.css'
1051
- ```
1052
-
1053
- ```html
1054
- <!-- Or in HTML -->
1055
- <link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">
1056
- ```
386
+ Works with any framework or vanilla JS:
1057
387
 
1058
- 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.
1059
-
1060
- ---
1061
-
1062
- ## Framework Integration
1063
-
1064
- ### React
1065
-
1066
- ```jsx
1067
- import { tws, twsx } from 'tailwind-to-style'
1068
- import { useEffect } from 'react'
1069
-
1070
- function App() {
1071
- // Inject CSS on mount
1072
- useEffect(() => {
1073
- twsx({
1074
- '.card': ['bg-white rounded-xl shadow-md p-6', {
1075
- '&:hover': 'shadow-xl',
1076
- '> .title': 'text-xl font-bold',
1077
- }]
1078
- })
1079
- }, [])
1080
-
1081
- return (
1082
- <div style={tws('flex items-center gap-4', true)}>
1083
- <button style={tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)}>
1084
- Click me
1085
- </button>
1086
- </div>
1087
- )
1088
- }
1089
- ```
1090
-
1091
- ### Vue
1092
-
1093
- ```vue
1094
- <script setup>
1095
- import 'tailwind-to-style/preflight.css'
1096
- import { tws, twsx } from 'tailwind-to-style'
1097
-
1098
- // twsx() at the top level of <script setup> is HMR-safe.
1099
- // When you edit the classes, Vite's HMR re-runs this block and
1100
- // the old CSS slot is replaced automatically โ€” no hard refresh needed.
1101
- twsx({
1102
- 'html': 'bg-gray-100 min-h-screen flex items-center justify-center',
1103
- '.card': [
1104
- 'bg-white p-5 border border-gray-300 rounded-xl shadow-md',
1105
- {
1106
- '&:hover': 'shadow-xl',
1107
- '> .title': 'text-xl font-bold text-gray-900 mb-3',
1108
- '> .body': 'text-sm text-gray-700',
1109
- },
1110
- ],
1111
- })
1112
- </script>
1113
-
1114
- <template>
1115
- <div class="card">
1116
- <div class="title">Card Title</div>
1117
- <p class="body">Styled with twsx โ€” hot reload works out of the box.</p>
1118
- </div>
1119
- </template>
1120
- ```
1121
-
1122
- For simple inline styles, use `tws()` with the reactive system:
1123
-
1124
- ```vue
1125
- <script setup>
1126
- import { tws } from 'tailwind-to-style'
1127
- const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
1128
- </script>
1129
-
1130
- <template>
1131
- <button :style="btnStyle">Click me</button>
1132
- </template>
1133
- ```
1134
-
1135
- ### Svelte
1136
-
1137
- ```svelte
1138
- <script>
1139
- import { tws } from 'tailwind-to-style'
1140
- const style = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
1141
- </script>
1142
-
1143
- <button style={Object.entries(style).map(([k,v]) => `${k}:${v}`).join(';')}>
1144
- Click me
1145
- </button>
1146
- ```
1147
-
1148
- ### Vanilla JS
1149
-
1150
- ```javascript
1151
- import { tws, twsx } from 'tailwind-to-style'
1152
-
1153
- // Inline styles
1154
- const el = document.createElement('button')
1155
- Object.assign(el.style, tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true))
1156
-
1157
- // Inject global styles
1158
- twsx({
1159
- '.card': 'bg-white p-6 rounded-lg shadow-md',
1160
- '.card:hover': 'shadow-xl',
1161
- })
1162
- ```
1163
-
1164
- ---
1165
-
1166
- ## Performance
1167
-
1168
- v3.2.0 includes major performance optimizations:
1169
-
1170
- - **Pre-compiled regex** โ€” compiled once at module load, reused for every call
1171
- - **Multi-level LRU caching** โ€” class resolution, CSS generation, config lookups
1172
- - **Bounded caches** โ€” Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
1173
- - **Slot-based CSS injection** โ€” each `twsx()` call owns a named slot; updates rebuild the tag instead of appending, preventing HMR accumulation
1174
- - **FNV-1a hashing** โ€” 100x faster than `JSON.stringify` for cache keys
1175
-
1176
- ```
1177
- Parse 10,000 classes:
1178
- Cold: ~12ms
1179
- Cached: ~0.12ms (100x faster)
1180
-
1181
- Bundle sizes:
1182
- Full import: ~12KB minified
1183
- tws() only: ~3KB minified
1184
- twsx() only: ~6KB minified
1185
- ```
1186
-
1187
- **Performance utilities:**
1188
-
1189
- ```javascript
1190
- import { performanceUtils } from 'tailwind-to-style'
1191
-
1192
- // View cache stats
1193
- performanceUtils.getStats()
1194
-
1195
- // Clear all caches
1196
- performanceUtils.clearCaches()
1197
-
1198
- // Enable performance logging
1199
- performanceUtils.enablePerformanceLogging(true)
1200
- ```
1201
-
1202
- ---
1203
-
1204
- ## Debugging & Logging
1205
-
1206
- Logging is disabled by default. Enable via environment variable or programmatically:
1207
-
1208
- ```bash
1209
- TWSX_LOG_LEVEL=debug npm start # debug, info, warn, error, silent
1210
- ```
1211
-
1212
- ```javascript
1213
- import { logger } from 'tailwind-to-style'
1214
-
1215
- logger.setLevel('debug')
1216
- console.log(logger.getLevel()) // โ†’ 'debug'
1217
- ```
1218
-
1219
- | Level | Description |
1220
- |---|---|
1221
- | `debug` | Detailed processing info |
1222
- | `info` | General information |
1223
- | `warn` | Performance warnings |
1224
- | `error` | Errors only |
1225
- | `silent` | No logging (default) |
1226
-
1227
- ---
1228
-
1229
- ## Comparison
1230
-
1231
- | Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS | tailwind-variants |
1232
- |---|:---:|:---:|:---:|:---:|
1233
- | Build Step | None | Required | None | Required |
1234
- | Bundle Size | 3-12KB | ~80KB+ | 20-40KB | ~15KB |
1235
- | Runtime Styles | Yes | No | Yes | Partial |
1236
- | Full Tailwind Support | Yes | Yes | No | Classes only |
1237
- | SSR Support | Yes | Yes | Depends | Yes |
1238
- | Variant System | Built-in | No | No | Yes |
1239
- | Conditional Classes | `cx()` | No | No | `tv()` |
1240
- | SCSS-like Nesting | Yes | Plugins | Yes | No |
1241
- | @css Raw Injection | Yes | No | Yes | No |
1242
- | Framework Agnostic | Yes | Yes | Depends | Yes |
1243
- | Tree-Shaking | Yes | Partial | Yes | Yes |
1244
- | TypeScript Generics | Yes | Yes | Yes | Yes |
1245
- | Zero Dependencies | Yes | PostCSS | No | tailwind-merge |
1246
-
1247
- ---
1248
-
1249
- ## Migration from v2
1250
-
1251
- See [MIGRATION.md](MIGRATION.md) for the detailed guide from v2.x to v3.x.
1252
-
1253
- **Quick summary:**
1254
-
1255
- | Status | API |
1256
- |---|---|
1257
- | No changes | `tws()`, `twsx()`, `configure()` |
1258
- | New in v3.1 | `twsxVariants()` |
1259
- | New in v3.2 | `cx()`, SSR, tree-shakeable imports |
1260
- | Removed | `styled()`, `tv()`, `useTwsx()`, `TwsxProvider`, CLI tools |
1261
-
1262
- ---
1263
-
1264
- ## Contributing
1265
-
1266
- Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for architecture overview, testing guidelines, and build output docs.
1267
-
1268
- ---
1269
-
1270
- ## ๐Ÿ’– Support
1271
-
1272
- If you find this library helpful, consider supporting:
1273
-
1274
- [โ˜• Buy me a coffee](https://buymeacoffee.com/bigetion)
388
+ - **React** โ€” Full bindings via `tailwind-to-style/react` (example available in `examples/react-demo`)
389
+ - **Vanilla JS** โ€” Direct DOM usage with `tw()` and `tws()`
390
+ - **Node.js / SSR** โ€” `tws()` for inline styles + `createSSRCollector()` for CSS extraction
391
+ - **Vue / Svelte** โ€” supported in runtime with `tw()` / `tws()`, examples can be added in future releases
1275
392
 
1276
393
  ---
1277
394
 
1278
395
  ## License
1279
396
 
1280
397
  MIT ยฉ [Bigetion](https://github.com/Bigetion)
1281
-
1282
- ---
1283
-
1284
- **v3.2.0** โ€” [Changelog](CHANGELOG.md) ยท [Architecture](ARCHITECTURE.md) ยท [Migration Guide](MIGRATION.md)