tailwind-to-style 3.2.2 โ 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -666
- package/dist/animations/index.cjs +9391 -0
- package/dist/animations/index.d.ts +58 -0
- package/dist/animations/index.esm.js +9385 -0
- package/dist/animations/index.esm.js.map +1 -0
- package/dist/className/index.cjs +9080 -0
- package/dist/className/index.esm.js +9075 -0
- package/dist/className/index.esm.js.map +1 -0
- package/dist/core/tws.cjs +136 -114
- package/dist/core/tws.cjs.map +1 -0
- package/dist/core/tws.esm.js +136 -114
- package/dist/core/tws.esm.js.map +1 -1
- package/dist/core/twsx.cjs +2442 -4245
- package/dist/core/twsx.esm.js +2442 -4245
- package/dist/core/twsx.esm.js.map +1 -1
- package/dist/core/twsxVariants.cjs +2470 -4262
- package/dist/core/twsxVariants.esm.js +2470 -4262
- package/dist/core/twsxVariants.esm.js.map +1 -1
- package/dist/cx.cjs +2 -2
- package/dist/cx.cjs.map +1 -0
- package/dist/cx.esm.js +2 -2
- package/dist/index.cjs +5128 -6057
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +993 -1
- package/dist/index.esm.js +5124 -6022
- package/dist/index.esm.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/react/index.cjs +10177 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.ts +69 -0
- package/dist/react/index.esm.js +10173 -0
- package/dist/react/index.esm.js.map +1 -0
- package/dist/styled/index.cjs +9094 -0
- package/dist/styled/index.cjs.map +1 -0
- package/dist/styled/index.d.ts +17 -0
- package/dist/styled/index.esm.js +9087 -0
- package/dist/styled/index.esm.js.map +1 -0
- package/dist/tokens/index.cjs +359 -0
- package/dist/tokens/index.d.ts +33 -0
- package/dist/tokens/index.esm.js +355 -0
- package/dist/tokens/index.esm.js.map +1 -0
- package/dist/utils/index.cjs +313 -297
- package/dist/utils/index.esm.js +313 -297
- package/dist/utils/index.esm.js.map +1 -1
- package/package.json +38 -24
- package/types/animations/index.d.ts +58 -0
- package/types/className/index.d.ts +41 -0
- package/types/index.d.ts +993 -1
- package/types/react/index.d.ts +69 -0
- package/types/tokens/index.d.ts +33 -0
- package/types/v4.d.ts +191 -0
package/README.md
CHANGED
|
@@ -1,65 +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
|
[](https://www.npmjs.com/package/tailwind-to-style)
|
|
8
|
-
[](https://github.com/Bigetion/tailwind-to-style/actions)
|
|
9
|
-
[](https://www.npmjs.com/package/tailwind-to-style)
|
|
10
4
|
[](https://bundlephobia.com/package/tailwind-to-style)
|
|
11
5
|
[](https://github.com/Bigetion/tailwind-to-style/blob/main/LICENSE)
|
|
12
6
|
|
|
13
|
-
|
|
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
|
-
- [`cx()` โ Conditional Class Builder](#cx--conditional-class-builder)
|
|
28
|
-
- [Configuration & Plugins](#configuration--plugins)
|
|
29
|
-
- [`configure()` โ Custom Theme](#configure--custom-theme)
|
|
30
|
-
- [Plugin System](#plugin-system)
|
|
31
|
-
- [SSR (Server-Side Rendering)](#ssr-server-side-rendering)
|
|
32
|
-
- [Animation System](#animation-system)
|
|
33
|
-
- [Tree-Shakeable Imports](#tree-shakeable-imports)
|
|
34
|
-
- [Preflight CSS](#preflight-css)
|
|
35
|
-
- [Framework Integration](#framework-integration)
|
|
36
|
-
- [Performance](#performance)
|
|
37
|
-
- [Debugging & Logging](#debugging--logging)
|
|
38
|
-
- [Comparison](#comparison)
|
|
39
|
-
- [Migration from v2](#migration-from-v2)
|
|
40
|
-
- [Contributing](#contributing)
|
|
41
|
-
- [Support](#-support)
|
|
42
|
-
- [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.
|
|
43
8
|
|
|
44
9
|
---
|
|
45
10
|
|
|
46
11
|
## Why tailwind-to-style?
|
|
47
12
|
|
|
48
|
-
| Feature |
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
| **Tree-Shakeable** | Import only what you need โ reduce bundle by 50-70% |
|
|
61
|
-
| **Lightweight** | ~12KB minified, zero runtime dependencies |
|
|
62
|
-
| **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 | โ
| โ
|
|
|
63
25
|
|
|
64
26
|
---
|
|
65
27
|
|
|
@@ -70,750 +32,343 @@ npm install tailwind-to-style
|
|
|
70
32
|
```
|
|
71
33
|
|
|
72
34
|
```bash
|
|
73
|
-
|
|
35
|
+
pnpm add tailwind-to-style
|
|
74
36
|
```
|
|
75
37
|
|
|
76
38
|
```bash
|
|
77
|
-
|
|
39
|
+
yarn add tailwind-to-style
|
|
78
40
|
```
|
|
79
41
|
|
|
80
|
-
|
|
42
|
+
Or use a CDN:
|
|
81
43
|
|
|
82
44
|
```html
|
|
83
45
|
<script src="https://unpkg.com/tailwind-to-style"></script>
|
|
84
|
-
<script>
|
|
85
|
-
const { tws, twsx } = tailwindToStyle
|
|
86
|
-
</script>
|
|
87
46
|
```
|
|
88
47
|
|
|
89
48
|
---
|
|
90
49
|
|
|
91
50
|
## Quick Start
|
|
92
51
|
|
|
93
|
-
```
|
|
94
|
-
import {
|
|
95
|
-
|
|
96
|
-
// 1. Inline styles
|
|
97
|
-
const style = tws('bg-blue-500 text-white p-4 rounded-lg', true)
|
|
98
|
-
// โ { backgroundColor: '#3b82f6', color: '#fff', padding: '1rem', borderRadius: '0.5rem' }
|
|
99
|
-
|
|
100
|
-
// 2. Real CSS with selectors
|
|
101
|
-
twsx({
|
|
102
|
-
'.card': ['bg-white p-6 rounded-xl shadow-md', {
|
|
103
|
-
'&:hover': 'shadow-xl',
|
|
104
|
-
'> .title': 'text-xl font-bold text-gray-900',
|
|
105
|
-
}]
|
|
106
|
-
})
|
|
107
|
-
// โ auto-injects <style> with .card { ... } .card:hover { ... }
|
|
108
|
-
|
|
109
|
-
// 3. Component variants
|
|
110
|
-
const btn = twsxVariants('.btn', {
|
|
111
|
-
base: 'px-4 py-2 rounded-lg font-medium',
|
|
112
|
-
variants: {
|
|
113
|
-
color: { primary: 'bg-blue-500 text-white', danger: 'bg-red-500 text-white' },
|
|
114
|
-
size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
|
|
115
|
-
},
|
|
116
|
-
defaultVariants: { color: 'primary', size: 'md' },
|
|
117
|
-
})
|
|
118
|
-
btn({ color: 'danger', size: 'lg' }) // โ "btn btn-danger-lg"
|
|
52
|
+
```js
|
|
53
|
+
import { tw, tws, cx } from 'tailwind-to-style';
|
|
119
54
|
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
// โ 'p-4 bg-blue-500'
|
|
123
|
-
```
|
|
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');
|
|
124
57
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
## Core API
|
|
128
|
-
|
|
129
|
-
### `tws()` โ Tailwind to Inline Styles
|
|
58
|
+
// Convert to inline styles
|
|
59
|
+
element.style.cssText = tws('bg-blue-500 text-white p-4 rounded-lg');
|
|
130
60
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
import { tws } from 'tailwind-to-style'
|
|
135
|
-
|
|
136
|
-
// CSS string (default)
|
|
137
|
-
tws('bg-blue-500 p-4 rounded-lg')
|
|
138
|
-
// โ "background-color: #3b82f6; padding: 1rem; border-radius: 0.5rem;"
|
|
139
|
-
|
|
140
|
-
// JSON object (pass `true` as 2nd argument)
|
|
141
|
-
tws('flex items-center gap-4', true)
|
|
142
|
-
// โ { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
**Supported features:**
|
|
146
|
-
|
|
147
|
-
```javascript
|
|
148
|
-
// Responsive breakpoints
|
|
149
|
-
tws('text-sm md:text-base lg:text-lg')
|
|
150
|
-
|
|
151
|
-
// Pseudo-states
|
|
152
|
-
tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
|
|
153
|
-
|
|
154
|
-
// Arbitrary values
|
|
155
|
-
tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')
|
|
156
|
-
|
|
157
|
-
// Important modifier
|
|
158
|
-
tws('!bg-red-500 !text-white')
|
|
159
|
-
|
|
160
|
-
// Negative values
|
|
161
|
-
tws('-mt-4 -translate-x-2')
|
|
162
|
-
|
|
163
|
-
// Opacity modifier
|
|
164
|
-
tws('bg-blue-500/50 text-black/75')
|
|
165
|
-
|
|
166
|
-
// Decimal spacing
|
|
167
|
-
tws('p-0.5 m-1.5 gap-2.5')
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
**Use in React:**
|
|
171
|
-
|
|
172
|
-
```jsx
|
|
173
|
-
<div style={tws('flex items-center gap-4 bg-white p-6 rounded-xl shadow-md', true)}>
|
|
174
|
-
<h1 style={tws('text-2xl font-bold text-gray-900', true)}>Hello</h1>
|
|
175
|
-
</div>
|
|
61
|
+
// Conditional class merging
|
|
62
|
+
const classes = cx('base', isActive && 'ring-2', { 'opacity-50': disabled });
|
|
176
63
|
```
|
|
177
64
|
|
|
178
65
|
---
|
|
179
66
|
|
|
180
|
-
|
|
67
|
+
## API Reference
|
|
181
68
|
|
|
182
|
-
|
|
69
|
+
### `tw()` โ The Main Function
|
|
183
70
|
|
|
184
|
-
|
|
71
|
+
One function, four modes:
|
|
185
72
|
|
|
186
|
-
|
|
187
|
-
import { twsx } from 'tailwind-to-style'
|
|
73
|
+
#### Mode 1: String โ Atomic Classes
|
|
188
74
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
'&:hover': 'bg-blue-600 shadow-lg transform scale-105',
|
|
194
|
-
'&:active': 'bg-blue-700 scale-95',
|
|
195
|
-
'&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
|
|
196
|
-
'&.large': 'px-8 py-4 text-lg',
|
|
197
|
-
}
|
|
198
|
-
],
|
|
199
|
-
|
|
200
|
-
'.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
201
|
-
'.card > .header': 'p-6 border-b border-gray-200',
|
|
202
|
-
'.card > .body': 'p-6',
|
|
203
|
-
|
|
204
|
-
// Media queries
|
|
205
|
-
'@media (max-width: 768px)': {
|
|
206
|
-
'.card': 'rounded-lg',
|
|
207
|
-
'.card > .header': 'p-4',
|
|
208
|
-
}
|
|
209
|
-
})
|
|
75
|
+
```js
|
|
76
|
+
tw('flex items-center gap-4 hover:bg-gray-100')
|
|
77
|
+
// โ "tw-flex tw-items-center tw-gap-4 tw-hover-bg-gray-100"
|
|
78
|
+
// CSS is auto-injected with full pseudo-class support
|
|
210
79
|
```
|
|
211
80
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
| Pattern | Example | Description |
|
|
215
|
-
|---|---|---|
|
|
216
|
-
| `&:pseudo` | `'&:hover': 'bg-blue-600'` | Pseudo-classes |
|
|
217
|
-
| `&.modifier` | `'&.active': 'ring-2'` | Class modifiers |
|
|
218
|
-
| `> .child` | `'> .title': 'text-xl'` | Direct children |
|
|
219
|
-
| `.descendant` | `'.icon': 'w-5 h-5'` | Descendants |
|
|
220
|
-
| `@media` | `'@media (max-width: 768px)': { ... }` | Media queries |
|
|
221
|
-
|
|
222
|
-
**Options:**
|
|
81
|
+
#### Mode 2: Named Class
|
|
223
82
|
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
|
|
83
|
+
```js
|
|
84
|
+
tw('sidebar', 'w-64 h-screen bg-white border-r border-gray-200')
|
|
85
|
+
// โ "sidebar"
|
|
86
|
+
// Generates .sidebar { ... } with all the Tailwind styles
|
|
227
87
|
```
|
|
228
88
|
|
|
229
|
-
####
|
|
89
|
+
#### Mode 3: Variants
|
|
230
90
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
```javascript
|
|
236
|
-
twsx({
|
|
237
|
-
'.gradient-text': '@css { background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
|
|
238
|
-
})
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
**Object form (within arrays):**
|
|
242
|
-
|
|
243
|
-
```javascript
|
|
244
|
-
twsx({
|
|
245
|
-
'.gradient-text': [
|
|
246
|
-
'text-3xl font-bold',
|
|
247
|
-
{
|
|
248
|
-
'@css': {
|
|
249
|
-
'background': 'linear-gradient(90deg, #ff6b6b, #feca57)',
|
|
250
|
-
'-webkit-background-clip': 'text',
|
|
251
|
-
'-webkit-text-fill-color': 'transparent',
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
})
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
|
-
### `twsxVariants()` โ Component Variant System
|
|
261
|
-
|
|
262
|
-
A CVA-like API for building type-safe component variants. Auto-generates CSS for all combinations and returns a class name builder function.
|
|
263
|
-
|
|
264
|
-
```javascript
|
|
265
|
-
import { twsxVariants } from 'tailwind-to-style'
|
|
266
|
-
|
|
267
|
-
const btn = twsxVariants('.btn', {
|
|
268
|
-
base: 'px-4 py-2 rounded-lg font-medium transition-all border',
|
|
91
|
+
```js
|
|
92
|
+
const button = tw({
|
|
93
|
+
name: 'btn',
|
|
94
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-all',
|
|
269
95
|
variants: {
|
|
270
|
-
variant: {
|
|
271
|
-
solid: 'shadow-sm',
|
|
272
|
-
outline: 'bg-transparent border-2',
|
|
273
|
-
ghost: 'bg-transparent border-transparent',
|
|
274
|
-
},
|
|
275
96
|
color: {
|
|
276
|
-
primary: 'bg-blue-
|
|
277
|
-
danger: 'bg-red-
|
|
278
|
-
|
|
97
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
98
|
+
danger: 'bg-red-600 text-white hover:bg-red-700',
|
|
99
|
+
ghost: 'bg-transparent text-gray-700 hover:bg-gray-100',
|
|
279
100
|
},
|
|
280
101
|
size: {
|
|
281
|
-
sm: 'px-3 py-1.5
|
|
282
|
-
md: 'px-4 py-2
|
|
283
|
-
lg: 'px-6 py-3
|
|
284
|
-
},
|
|
285
|
-
disabled: {
|
|
286
|
-
true: 'opacity-50 cursor-not-allowed pointer-events-none',
|
|
102
|
+
sm: 'text-sm px-3 py-1.5',
|
|
103
|
+
md: 'text-base px-4 py-2',
|
|
104
|
+
lg: 'text-lg px-6 py-3',
|
|
287
105
|
},
|
|
288
106
|
},
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
{ variant: 'outline', color: 'danger', class: 'bg-transparent text-red-600 border-red-500' },
|
|
292
|
-
],
|
|
293
|
-
defaultVariants: { variant: 'solid', color: 'primary', size: 'md' },
|
|
294
|
-
})
|
|
107
|
+
defaultVariants: { color: 'primary', size: 'md' },
|
|
108
|
+
});
|
|
295
109
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
btn({ color: 'danger' }) // "btn btn-danger"
|
|
299
|
-
btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
|
|
110
|
+
button({ color: 'danger', size: 'lg' })
|
|
111
|
+
// โ "btn btn--color-danger btn--size-lg"
|
|
300
112
|
```
|
|
301
113
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
```javascript
|
|
305
|
-
const alert = twsxVariants('.alert', {
|
|
306
|
-
base: 'p-4 rounded-lg border flex gap-3',
|
|
307
|
-
variants: {
|
|
308
|
-
status: {
|
|
309
|
-
info: 'bg-blue-50 border-blue-200 text-blue-800',
|
|
310
|
-
error: 'bg-red-50 border-red-200 text-red-800',
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
defaultVariants: { status: 'info' },
|
|
314
|
-
nested: {
|
|
315
|
-
'.alert-icon': 'flex-shrink-0 mt-0.5',
|
|
316
|
-
'.alert-content': 'flex-1',
|
|
317
|
-
'.alert-dismiss': 'p-1 rounded hover:bg-black/10 cursor-pointer',
|
|
318
|
-
}
|
|
319
|
-
})
|
|
320
|
-
// Generates: .alert .alert-icon { ... }, .alert .alert-content { ... }, etc.
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
**Class naming convention:**
|
|
324
|
-
|
|
325
|
-
| Call | Returns | Why |
|
|
326
|
-
|---|---|---|
|
|
327
|
-
| `btn()` | `"btn"` | All defaults |
|
|
328
|
-
| `btn({ color: 'danger' })` | `"btn btn-danger"` | One non-default |
|
|
329
|
-
| `btn({ variant: 'outline', color: 'danger', size: 'lg' })` | `"btn btn-outline-danger-lg"` | All non-defaults |
|
|
330
|
-
|
|
331
|
-
**TypeScript โ full generics support:**
|
|
332
|
-
|
|
333
|
-
```typescript
|
|
334
|
-
import { twsxVariants, type VariantProps } from 'tailwind-to-style'
|
|
114
|
+
#### Mode 4: Slots (Multi-Part Components)
|
|
335
115
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
116
|
+
```js
|
|
117
|
+
const card = tw({
|
|
118
|
+
name: 'card',
|
|
119
|
+
slots: {
|
|
120
|
+
root: 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
121
|
+
header: 'px-6 py-4 border-b border-gray-100',
|
|
122
|
+
body: 'px-6 py-4',
|
|
123
|
+
footer: 'px-6 py-4 bg-gray-50',
|
|
341
124
|
},
|
|
342
|
-
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
type ButtonProps = VariantProps<typeof button>
|
|
346
|
-
// โ { variant?: 'solid' | 'outline', size?: 'sm' | 'md' | 'lg' }
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
---
|
|
350
|
-
|
|
351
|
-
### `cx()` โ Conditional Class Builder
|
|
352
|
-
|
|
353
|
-
A built-in utility for conditionally joining class names โ replaces `clsx`/`classnames`:
|
|
354
|
-
|
|
355
|
-
```javascript
|
|
356
|
-
import { cx } from 'tailwind-to-style'
|
|
357
|
-
|
|
358
|
-
// Strings
|
|
359
|
-
cx('bg-blue-500', 'text-white')
|
|
360
|
-
// โ 'bg-blue-500 text-white'
|
|
361
|
-
|
|
362
|
-
// Conditionals
|
|
363
|
-
cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
|
|
364
|
-
// โ 'p-4 bg-blue-500'
|
|
365
|
-
|
|
366
|
-
// Object syntax
|
|
367
|
-
cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled })
|
|
368
|
-
// โ 'p-4 bg-blue-500'
|
|
125
|
+
});
|
|
369
126
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
// โ 'p-4 bg-white ring-2 ring-blue-500'
|
|
373
|
-
|
|
374
|
-
// Combined with tws()
|
|
375
|
-
const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))
|
|
127
|
+
card()
|
|
128
|
+
// โ { root: "card__root", header: "card__header", body: "card__body", footer: "card__footer" }
|
|
376
129
|
```
|
|
377
130
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
```javascript
|
|
381
|
-
const btnClass = cx.with('px-4 py-2 rounded font-medium transition-colors')
|
|
382
|
-
|
|
383
|
-
btnClass('bg-blue-500 text-white')
|
|
384
|
-
// โ 'px-4 py-2 rounded font-medium transition-colors bg-blue-500 text-white'
|
|
131
|
+
#### Utility Methods
|
|
385
132
|
|
|
386
|
-
|
|
387
|
-
//
|
|
133
|
+
```js
|
|
134
|
+
tw.extractCSS() // Get all generated CSS as string (SSR)
|
|
135
|
+
tw.clearCache() // Clear internal caches
|
|
136
|
+
tw.config({ prefix: 'my', hash: false }) // Configure globally
|
|
388
137
|
```
|
|
389
138
|
|
|
390
139
|
---
|
|
391
140
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
### `configure()` โ Custom Theme
|
|
395
|
-
|
|
396
|
-
Extend the default Tailwind theme with custom colors, spacing, fonts, and more.
|
|
397
|
-
|
|
398
|
-
```javascript
|
|
399
|
-
import { configure } from 'tailwind-to-style'
|
|
400
|
-
|
|
401
|
-
configure({
|
|
402
|
-
theme: {
|
|
403
|
-
extend: {
|
|
404
|
-
colors: {
|
|
405
|
-
brand: {
|
|
406
|
-
50: '#eff6ff',
|
|
407
|
-
100: '#dbeafe',
|
|
408
|
-
500: '#3b82f6',
|
|
409
|
-
600: '#2563eb',
|
|
410
|
-
900: '#1e3a8a',
|
|
411
|
-
},
|
|
412
|
-
accent: '#f59e0b',
|
|
413
|
-
},
|
|
414
|
-
spacing: {
|
|
415
|
-
'128': '32rem',
|
|
416
|
-
'144': '36rem',
|
|
417
|
-
},
|
|
418
|
-
fontFamily: {
|
|
419
|
-
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
420
|
-
},
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
})
|
|
141
|
+
### `tws()` โ Inline Styles
|
|
424
142
|
|
|
425
|
-
|
|
426
|
-
tws('bg-brand-500 text-brand-50 p-128 font-sans')
|
|
427
|
-
```
|
|
143
|
+
Convert Tailwind classes directly to CSS styles. No DOM injection needed.
|
|
428
144
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
| `configure(config)` | Apply custom configuration |
|
|
434
|
-
| `getConfig()` | Get current configuration |
|
|
435
|
-
| `resetConfig()` | Reset to defaults |
|
|
436
|
-
| `clearConfigCache()` | Clear cached config lookups |
|
|
437
|
-
|
|
438
|
-
### Plugin System
|
|
439
|
-
|
|
440
|
-
Create reusable plugins to extend the utility set:
|
|
441
|
-
|
|
442
|
-
```javascript
|
|
443
|
-
import { configure, createPlugin, createUtilityPlugin } from 'tailwind-to-style'
|
|
145
|
+
```js
|
|
146
|
+
// Returns CSS string
|
|
147
|
+
tws('bg-blue-500 p-4 rounded-lg')
|
|
148
|
+
// โ "background-color: rgb(59,130,246); padding: 1rem; border-radius: 0.5rem;"
|
|
444
149
|
|
|
445
|
-
//
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
'text-shadow-sm': { textShadow: '0 1px 2px rgba(0,0,0,0.05)' },
|
|
449
|
-
'text-shadow-md': { textShadow: '0 2px 4px rgba(0,0,0,0.1)' },
|
|
450
|
-
'text-shadow-lg': { textShadow: '0 4px 8px rgba(0,0,0,0.15)' },
|
|
451
|
-
},
|
|
452
|
-
})
|
|
453
|
-
|
|
454
|
-
// Dynamic plugin โ value-based utilities
|
|
455
|
-
const glass = createUtilityPlugin('glass', {
|
|
456
|
-
prefix: 'glass',
|
|
457
|
-
values: { sm: '4px', md: '8px', lg: '16px' },
|
|
458
|
-
formatter: (value) => ({
|
|
459
|
-
backdropFilter: `blur(${value})`,
|
|
460
|
-
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
461
|
-
}),
|
|
462
|
-
})
|
|
463
|
-
|
|
464
|
-
configure({ plugins: [textShadow, glass] })
|
|
465
|
-
|
|
466
|
-
// Now use custom utilities
|
|
467
|
-
tws('text-shadow-md glass-lg')
|
|
150
|
+
// Returns JSON object (for React style prop, etc.)
|
|
151
|
+
tws('flex items-center gap-4', true)
|
|
152
|
+
// โ { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
468
153
|
```
|
|
469
154
|
|
|
470
155
|
---
|
|
471
156
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
Collect CSS during server-side rendering instead of injecting into the DOM:
|
|
475
|
-
|
|
476
|
-
```javascript
|
|
477
|
-
import { startSSR, stopSSR, getSSRStyles, twsx } from 'tailwind-to-style'
|
|
157
|
+
### `cx()` โ Conditional Class Names
|
|
478
158
|
|
|
479
|
-
|
|
480
|
-
startSSR()
|
|
159
|
+
A lightweight `clsx` alternative built-in.
|
|
481
160
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
const html = renderToString(<App />)
|
|
161
|
+
```js
|
|
162
|
+
cx('base-class', isActive && 'active-class', { 'disabled': isDisabled })
|
|
163
|
+
// โ "base-class active-class"
|
|
486
164
|
|
|
487
|
-
//
|
|
488
|
-
|
|
165
|
+
// Arrays work too
|
|
166
|
+
cx(['p-4', 'bg-white'], condition && ['ring-2', 'ring-blue-500'])
|
|
489
167
|
|
|
490
|
-
//
|
|
491
|
-
const
|
|
492
|
-
|
|
493
|
-
<head><style>${css}</style></head>
|
|
494
|
-
<body>${html}</body>
|
|
495
|
-
</html>
|
|
496
|
-
`
|
|
168
|
+
// Create pre-filled cx
|
|
169
|
+
const btnClass = cx.with('px-4 py-2 rounded font-medium');
|
|
170
|
+
btnClass('bg-blue-500') // โ "px-4 py-2 rounded font-medium bg-blue-500"
|
|
497
171
|
```
|
|
498
172
|
|
|
499
|
-
**SSR API:**
|
|
500
|
-
|
|
501
|
-
| Function | Description |
|
|
502
|
-
|---|---|
|
|
503
|
-
| `startSSR()` | Begin collecting CSS |
|
|
504
|
-
| `stopSSR()` | Stop collecting, return all CSS as string |
|
|
505
|
-
| `getSSRStyles()` | Peek at collected CSS without stopping |
|
|
506
|
-
| `IS_BROWSER` | `true` in browser environment |
|
|
507
|
-
| `IS_SERVER` | `true` in Node.js/server environment |
|
|
508
|
-
|
|
509
173
|
---
|
|
510
174
|
|
|
511
|
-
##
|
|
512
|
-
|
|
513
|
-
### Built-in CSS Animations
|
|
514
|
-
|
|
515
|
-
```javascript
|
|
516
|
-
tws('animate-spin') // โ animation: spin 1s linear infinite
|
|
517
|
-
tws('animate-bounce') // โ animation: bounce 1s infinite
|
|
518
|
-
tws('animate-pulse') // โ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
|
|
519
|
-
tws('animate-ping') // โ animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
|
|
520
|
-
```
|
|
521
|
-
|
|
522
|
-
### Web Animations API
|
|
523
|
-
|
|
524
|
-
```javascript
|
|
525
|
-
import { applyWebAnimation } from 'tailwind-to-style'
|
|
526
|
-
|
|
527
|
-
// Apply a named animation to a DOM element
|
|
528
|
-
applyWebAnimation(element, 'fadeIn')
|
|
529
|
-
applyWebAnimation(element, 'slideUp')
|
|
530
|
-
```
|
|
531
|
-
|
|
532
|
-
### Inline Animations
|
|
533
|
-
|
|
534
|
-
```javascript
|
|
535
|
-
import { applyInlineAnimation, animateElement, chainAnimations, staggerAnimations } from 'tailwind-to-style'
|
|
175
|
+
## React Bindings
|
|
536
176
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
// Programmatic animation
|
|
541
|
-
animateElement(element, { opacity: [0, 1] }, { duration: 300 })
|
|
542
|
-
|
|
543
|
-
// Sequential chain
|
|
544
|
-
chainAnimations(element, ['fadeIn', 'slideUp', 'bounceIn'])
|
|
545
|
-
|
|
546
|
-
// Staggered across multiple elements
|
|
547
|
-
staggerAnimations('.card', 'fadeIn', { delay: 100 })
|
|
177
|
+
```bash
|
|
178
|
+
import { styled, ThemeProvider, useTheme, useTws } from 'tailwind-to-style/react';
|
|
548
179
|
```
|
|
549
180
|
|
|
550
|
-
|
|
181
|
+
### `styled()` โ Create Styled Components
|
|
551
182
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
Import only what you need to reduce bundle size by **50-70%**:
|
|
183
|
+
```jsx
|
|
184
|
+
import { styled } from 'tailwind-to-style/react';
|
|
555
185
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
186
|
+
const Button = styled('button', {
|
|
187
|
+
name: 'btn',
|
|
188
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-colors',
|
|
189
|
+
variants: {
|
|
190
|
+
color: {
|
|
191
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
192
|
+
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
|
|
193
|
+
},
|
|
194
|
+
size: {
|
|
195
|
+
sm: 'text-sm px-3 py-1.5',
|
|
196
|
+
lg: 'text-lg px-6 py-3',
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
defaultVariants: { color: 'primary', size: 'sm' },
|
|
200
|
+
});
|
|
562
201
|
|
|
563
|
-
//
|
|
564
|
-
|
|
202
|
+
// Variant props are type-safe and stripped from DOM
|
|
203
|
+
<Button color="primary" size="lg" onClick={handleClick}>
|
|
204
|
+
Click Me
|
|
205
|
+
</Button>
|
|
565
206
|
```
|
|
566
207
|
|
|
567
|
-
|
|
568
|
-
|---|---|---|
|
|
569
|
-
| `tailwind-to-style` | Everything | ~12KB |
|
|
570
|
-
| `tailwind-to-style/tws` | `tws()` only | ~3KB |
|
|
571
|
-
| `tailwind-to-style/twsx` | `twsx()` | ~6KB |
|
|
572
|
-
| `tailwind-to-style/twsx-variants` | `twsxVariants()` | ~6KB |
|
|
573
|
-
| `tailwind-to-style/cx` | `cx()` | <1KB |
|
|
574
|
-
| `tailwind-to-style/utils` | Logger, LRUCache, error handler | ~2KB |
|
|
575
|
-
|
|
576
|
-
All sub-paths provide ESM + CJS bundles with TypeScript type definitions.
|
|
208
|
+
### `ThemeProvider` & `useTheme`
|
|
577
209
|
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
## Preflight CSS
|
|
210
|
+
```jsx
|
|
211
|
+
import { ThemeProvider, useTheme } from 'tailwind-to-style/react';
|
|
581
212
|
|
|
582
|
-
|
|
213
|
+
const theme = {
|
|
214
|
+
colors: { primary: '#3b82f6', danger: '#ef4444' },
|
|
215
|
+
radius: { sm: '0.25rem', md: '0.5rem' },
|
|
216
|
+
};
|
|
583
217
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
218
|
+
function App() {
|
|
219
|
+
return (
|
|
220
|
+
<ThemeProvider theme={theme}>
|
|
221
|
+
<MyComponent />
|
|
222
|
+
</ThemeProvider>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
587
225
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
226
|
+
function MyComponent() {
|
|
227
|
+
const { theme, setTheme } = useTheme();
|
|
228
|
+
// Access tokens via CSS variables: var(--tws-colors-primary)
|
|
229
|
+
}
|
|
591
230
|
```
|
|
592
231
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
---
|
|
596
|
-
|
|
597
|
-
## Framework Integration
|
|
598
|
-
|
|
599
|
-
### React
|
|
232
|
+
### `useTws()` โ Inline Style Hook
|
|
600
233
|
|
|
601
234
|
```jsx
|
|
602
|
-
import {
|
|
603
|
-
import { useEffect } from 'react'
|
|
604
|
-
|
|
605
|
-
function App() {
|
|
606
|
-
// Inject CSS on mount
|
|
607
|
-
useEffect(() => {
|
|
608
|
-
twsx({
|
|
609
|
-
'.card': ['bg-white rounded-xl shadow-md p-6', {
|
|
610
|
-
'&:hover': 'shadow-xl',
|
|
611
|
-
'> .title': 'text-xl font-bold',
|
|
612
|
-
}]
|
|
613
|
-
})
|
|
614
|
-
}, [])
|
|
235
|
+
import { useTws } from 'tailwind-to-style/react';
|
|
615
236
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
Click me
|
|
620
|
-
</button>
|
|
621
|
-
</div>
|
|
622
|
-
)
|
|
237
|
+
function Box({ classes }) {
|
|
238
|
+
const style = useTws(classes); // memoized style object
|
|
239
|
+
return <div style={style}>Content</div>;
|
|
623
240
|
}
|
|
624
241
|
```
|
|
625
242
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
```vue
|
|
629
|
-
<script setup>
|
|
630
|
-
import 'tailwind-to-style/preflight.css'
|
|
631
|
-
import { tws, twsx } from 'tailwind-to-style'
|
|
632
|
-
|
|
633
|
-
// twsx() at the top level of <script setup> is HMR-safe.
|
|
634
|
-
// When you edit the classes, Vite's HMR re-runs this block and
|
|
635
|
-
// the old CSS slot is replaced automatically โ no hard refresh needed.
|
|
636
|
-
twsx({
|
|
637
|
-
'html': 'bg-gray-100 min-h-screen flex items-center justify-center',
|
|
638
|
-
'.card': [
|
|
639
|
-
'bg-white p-5 border border-gray-300 rounded-xl shadow-md',
|
|
640
|
-
{
|
|
641
|
-
'&:hover': 'shadow-xl',
|
|
642
|
-
'> .title': 'text-xl font-bold text-gray-900 mb-3',
|
|
643
|
-
'> .body': 'text-sm text-gray-700',
|
|
644
|
-
},
|
|
645
|
-
],
|
|
646
|
-
})
|
|
647
|
-
</script>
|
|
648
|
-
|
|
649
|
-
<template>
|
|
650
|
-
<div class="card">
|
|
651
|
-
<div class="title">Card Title</div>
|
|
652
|
-
<p class="body">Styled with twsx โ hot reload works out of the box.</p>
|
|
653
|
-
</div>
|
|
654
|
-
</template>
|
|
655
|
-
```
|
|
656
|
-
|
|
657
|
-
For simple inline styles, use `tws()` with the reactive system:
|
|
243
|
+
---
|
|
658
244
|
|
|
659
|
-
|
|
660
|
-
<script setup>
|
|
661
|
-
import { tws } from 'tailwind-to-style'
|
|
662
|
-
const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
|
|
663
|
-
</script>
|
|
245
|
+
## Design Tokens
|
|
664
246
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
</template>
|
|
247
|
+
```js
|
|
248
|
+
import { createTheme, tokenRegistry, token } from 'tailwind-to-style/tokens';
|
|
668
249
|
```
|
|
669
250
|
|
|
670
|
-
###
|
|
671
|
-
|
|
672
|
-
```svelte
|
|
673
|
-
<script>
|
|
674
|
-
import { tws } from 'tailwind-to-style'
|
|
675
|
-
const style = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
|
|
676
|
-
</script>
|
|
251
|
+
### `createTheme()`
|
|
677
252
|
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
253
|
+
```js
|
|
254
|
+
createTheme({
|
|
255
|
+
colors: {
|
|
256
|
+
primary: '#3b82f6',
|
|
257
|
+
secondary: '#8b5cf6',
|
|
258
|
+
success: '#10b981',
|
|
259
|
+
},
|
|
260
|
+
spacing: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
|
|
261
|
+
radius: { sm: '0.25rem', md: '0.5rem', lg: '1rem' },
|
|
262
|
+
});
|
|
263
|
+
// Injects CSS variables on :root:
|
|
264
|
+
// --tws-colors-primary: #3b82f6;
|
|
265
|
+
// --tws-colors-secondary: #8b5cf6;
|
|
266
|
+
// ...
|
|
681
267
|
```
|
|
682
268
|
|
|
683
|
-
###
|
|
269
|
+
### `tokenRegistry`
|
|
684
270
|
|
|
685
|
-
```
|
|
686
|
-
|
|
271
|
+
```js
|
|
272
|
+
tokenRegistry.get('colors.primary') // โ '#3b82f6'
|
|
273
|
+
tokenRegistry.set('colors.primary', '#2563eb')
|
|
274
|
+
tokenRegistry.toCSS() // โ full :root CSS string
|
|
275
|
+
tokenRegistry.subscribe((tokens) => { /* react to changes */ })
|
|
276
|
+
```
|
|
687
277
|
|
|
688
|
-
|
|
689
|
-
const el = document.createElement('button')
|
|
690
|
-
Object.assign(el.style, tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true))
|
|
278
|
+
### `token()` โ CSS Variable Reference
|
|
691
279
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
'.card:hover': 'shadow-xl',
|
|
696
|
-
})
|
|
280
|
+
```js
|
|
281
|
+
token('colors.primary') // โ "var(--tws-colors-primary)"
|
|
282
|
+
token('colors.primary', '#000') // โ "var(--tws-colors-primary, #000)"
|
|
697
283
|
```
|
|
698
284
|
|
|
699
285
|
---
|
|
700
286
|
|
|
701
|
-
##
|
|
287
|
+
## Animations
|
|
702
288
|
|
|
703
|
-
|
|
289
|
+
```js
|
|
290
|
+
import { animate, defineAnimation, getAnimationNames } from 'tailwind-to-style/animations';
|
|
291
|
+
```
|
|
704
292
|
|
|
705
|
-
|
|
706
|
-
- **Multi-level LRU caching** โ class resolution, CSS generation, config lookups
|
|
707
|
-
- **Bounded caches** โ Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
|
|
708
|
-
- **Slot-based CSS injection** โ each `twsx()` call owns a named slot; updates rebuild the tag instead of appending, preventing HMR accumulation
|
|
709
|
-
- **FNV-1a hashing** โ 100x faster than `JSON.stringify` for cache keys
|
|
293
|
+
### Built-in Presets
|
|
710
294
|
|
|
711
|
-
```
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
Bundle sizes:
|
|
717
|
-
Full import: ~12KB minified
|
|
718
|
-
tws() only: ~3KB minified
|
|
719
|
-
twsx() only: ~6KB minified
|
|
295
|
+
```js
|
|
296
|
+
element.className = animate('fadeIn');
|
|
297
|
+
element.className = animate('slideInUp', { duration: '500ms', delay: '100ms' });
|
|
298
|
+
element.className = animate('bounce');
|
|
299
|
+
element.className = animate('spin'); // infinite
|
|
720
300
|
```
|
|
721
301
|
|
|
722
|
-
|
|
302
|
+
Available presets: `fadeIn`, `fadeOut`, `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `scaleIn`, `scaleOut`, `bounce`, `shake`, `pulse`, `spin`, `ping`
|
|
723
303
|
|
|
724
|
-
|
|
725
|
-
import { performanceUtils } from 'tailwind-to-style'
|
|
304
|
+
### Custom Animations
|
|
726
305
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
306
|
+
```js
|
|
307
|
+
defineAnimation('wiggle', {
|
|
308
|
+
keyframes: [
|
|
309
|
+
{ transform: 'rotate(0deg)' },
|
|
310
|
+
{ transform: 'rotate(-3deg)' },
|
|
311
|
+
{ transform: 'rotate(3deg)' },
|
|
312
|
+
{ transform: 'rotate(0deg)' },
|
|
313
|
+
],
|
|
314
|
+
duration: '300ms',
|
|
315
|
+
easing: 'ease-in-out',
|
|
316
|
+
});
|
|
732
317
|
|
|
733
|
-
//
|
|
734
|
-
performanceUtils.enablePerformanceLogging(true)
|
|
318
|
+
animate('wiggle'); // works!
|
|
735
319
|
```
|
|
736
320
|
|
|
737
321
|
---
|
|
738
322
|
|
|
739
|
-
##
|
|
323
|
+
## SSR (Server-Side Rendering)
|
|
740
324
|
|
|
741
|
-
|
|
325
|
+
```js
|
|
326
|
+
import { tw, createSSRCollector } from 'tailwind-to-style';
|
|
742
327
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
```
|
|
328
|
+
// Collect all CSS generated during render
|
|
329
|
+
const collector = createSSRCollector();
|
|
746
330
|
|
|
747
|
-
|
|
748
|
-
|
|
331
|
+
const html = renderToString(<App />);
|
|
332
|
+
const css = collector.extract();
|
|
749
333
|
|
|
750
|
-
|
|
751
|
-
|
|
334
|
+
// Inject into HTML head
|
|
335
|
+
const fullHtml = `
|
|
336
|
+
<html>
|
|
337
|
+
<head><style>${css}</style></head>
|
|
338
|
+
<body>${html}</body>
|
|
339
|
+
</html>
|
|
340
|
+
`;
|
|
752
341
|
```
|
|
753
342
|
|
|
754
|
-
| Level | Description |
|
|
755
|
-
|---|---|
|
|
756
|
-
| `debug` | Detailed processing info |
|
|
757
|
-
| `info` | General information |
|
|
758
|
-
| `warn` | Performance warnings |
|
|
759
|
-
| `error` | Errors only |
|
|
760
|
-
| `silent` | No logging (default) |
|
|
761
|
-
|
|
762
|
-
---
|
|
763
|
-
|
|
764
|
-
## Comparison
|
|
765
|
-
|
|
766
|
-
| Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS | tailwind-variants |
|
|
767
|
-
|---|:---:|:---:|:---:|:---:|
|
|
768
|
-
| Build Step | None | Required | None | Required |
|
|
769
|
-
| Bundle Size | 3-12KB | ~80KB+ | 20-40KB | ~15KB |
|
|
770
|
-
| Runtime Styles | Yes | No | Yes | Partial |
|
|
771
|
-
| Full Tailwind Support | Yes | Yes | No | Classes only |
|
|
772
|
-
| SSR Support | Yes | Yes | Depends | Yes |
|
|
773
|
-
| Variant System | Built-in | No | No | Yes |
|
|
774
|
-
| Conditional Classes | `cx()` | No | No | `tv()` |
|
|
775
|
-
| SCSS-like Nesting | Yes | Plugins | Yes | No |
|
|
776
|
-
| @css Raw Injection | Yes | No | Yes | No |
|
|
777
|
-
| Framework Agnostic | Yes | Yes | Depends | Yes |
|
|
778
|
-
| Tree-Shaking | Yes | Partial | Yes | Yes |
|
|
779
|
-
| TypeScript Generics | Yes | Yes | Yes | Yes |
|
|
780
|
-
| Zero Dependencies | Yes | PostCSS | No | tailwind-merge |
|
|
781
|
-
|
|
782
343
|
---
|
|
783
344
|
|
|
784
|
-
##
|
|
785
|
-
|
|
786
|
-
See [MIGRATION.md](MIGRATION.md) for the detailed guide from v2.x to v3.x.
|
|
787
|
-
|
|
788
|
-
**Quick summary:**
|
|
789
|
-
|
|
790
|
-
| Status | API |
|
|
791
|
-
|---|---|
|
|
792
|
-
| No changes | `tws()`, `twsx()`, `configure()` |
|
|
793
|
-
| New in v3.1 | `twsxVariants()` |
|
|
794
|
-
| New in v3.2 | `cx()`, SSR, tree-shakeable imports |
|
|
795
|
-
| Removed | `styled()`, `tv()`, `useTwsx()`, `TwsxProvider`, CLI tools |
|
|
796
|
-
|
|
797
|
-
---
|
|
345
|
+
## Tree-Shakeable Imports
|
|
798
346
|
|
|
799
|
-
|
|
347
|
+
Import only what you need for minimal bundle size:
|
|
800
348
|
|
|
801
|
-
|
|
349
|
+
| Import Path | What You Get | ~Size |
|
|
350
|
+
|---|---|---|
|
|
351
|
+
| `tailwind-to-style` | `tw`, `tws`, `cx` | Full engine |
|
|
352
|
+
| `tailwind-to-style/react` | `styled`, `ThemeProvider`, `useTheme`, `useTws` | +2KB |
|
|
353
|
+
| `tailwind-to-style/tokens` | `createTheme`, `tokenRegistry`, `token` | +1KB |
|
|
354
|
+
| `tailwind-to-style/animations` | `animate`, `defineAnimation` | +1.5KB |
|
|
355
|
+
| `tailwind-to-style/cx` | `cx` only | ~300B |
|
|
356
|
+
| `tailwind-to-style/tws` | `tws` only | Subset |
|
|
802
357
|
|
|
803
358
|
---
|
|
804
359
|
|
|
805
|
-
##
|
|
360
|
+
## Framework Support
|
|
806
361
|
|
|
807
|
-
|
|
362
|
+
Works with any framework or vanilla JS:
|
|
808
363
|
|
|
809
|
-
|
|
364
|
+
- **React** โ Full bindings via `tailwind-to-style/react`
|
|
365
|
+
- **Vue** โ Use `tw()` in computed properties or `tws()` in `:style`
|
|
366
|
+
- **Svelte** โ Use `tw()` in `class:` or `tws()` in `style:`
|
|
367
|
+
- **Vanilla JS** โ Direct DOM manipulation
|
|
368
|
+
- **Node.js / SSR** โ `tws()` for inline + `createSSRCollector()` for classes
|
|
810
369
|
|
|
811
370
|
---
|
|
812
371
|
|
|
813
372
|
## License
|
|
814
373
|
|
|
815
374
|
MIT ยฉ [Bigetion](https://github.com/Bigetion)
|
|
816
|
-
|
|
817
|
-
---
|
|
818
|
-
|
|
819
|
-
**v3.2.0** โ [Changelog](CHANGELOG.md) ยท [Architecture](ARCHITECTURE.md) ยท [Migration Guide](MIGRATION.md)
|