tailwind-to-style 3.3.0 โ 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 +208 -1118
- 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 +2241 -4181
- package/dist/className/index.esm.js +2241 -4181
- package/dist/className/index.esm.js.map +1 -1
- 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 +1971 -3970
- package/dist/core/twsx.esm.js +1971 -3970
- package/dist/core/twsx.esm.js.map +1 -1
- package/dist/core/twsxVariants.cjs +1997 -3986
- package/dist/core/twsxVariants.esm.js +1997 -3986
- 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 +5253 -9252
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.esm.js +5251 -9201
- 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 +219 -270
- package/dist/utils/index.esm.js +219 -270
- package/dist/utils/index.esm.js.map +1 -1
- package/package.json +33 -24
- package/types/animations/index.d.ts +58 -0
- package/types/index.d.ts +4 -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,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
|
[](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
|
-
- [`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 |
|
|
51
|
-
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
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,343 @@ npm install tailwind-to-style
|
|
|
74
32
|
```
|
|
75
33
|
|
|
76
34
|
```bash
|
|
77
|
-
|
|
35
|
+
pnpm add tailwind-to-style
|
|
78
36
|
```
|
|
79
37
|
|
|
80
38
|
```bash
|
|
81
|
-
|
|
39
|
+
yarn add tailwind-to-style
|
|
82
40
|
```
|
|
83
41
|
|
|
84
|
-
|
|
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
|
-
```
|
|
98
|
-
import {
|
|
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
|
|
132
|
-
|
|
133
|
-
### `tws()` โ Tailwind to Inline Styles
|
|
52
|
+
```js
|
|
53
|
+
import { tw, tws, cx } from 'tailwind-to-style';
|
|
134
54
|
|
|
135
|
-
|
|
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');
|
|
136
57
|
|
|
137
|
-
|
|
138
|
-
|
|
58
|
+
// Convert to inline styles
|
|
59
|
+
element.style.cssText = tws('bg-blue-500 text-white p-4 rounded-lg');
|
|
139
60
|
|
|
140
|
-
//
|
|
141
|
-
|
|
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' }
|
|
147
|
-
```
|
|
148
|
-
|
|
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')
|
|
157
|
-
|
|
158
|
-
// Arbitrary values
|
|
159
|
-
tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')
|
|
160
|
-
|
|
161
|
-
// Important modifier
|
|
162
|
-
tws('!bg-red-500 !text-white')
|
|
163
|
-
|
|
164
|
-
// Negative values
|
|
165
|
-
tws('-mt-4 -translate-x-2')
|
|
166
|
-
|
|
167
|
-
// Opacity modifier
|
|
168
|
-
tws('bg-blue-500/50 text-black/75')
|
|
169
|
-
|
|
170
|
-
// Decimal spacing
|
|
171
|
-
tws('p-0.5 m-1.5 gap-2.5')
|
|
172
|
-
```
|
|
173
|
-
|
|
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>
|
|
61
|
+
// Conditional class merging
|
|
62
|
+
const classes = cx('base', isActive && 'ring-2', { 'opacity-50': disabled });
|
|
180
63
|
```
|
|
181
64
|
|
|
182
65
|
---
|
|
183
66
|
|
|
184
|
-
|
|
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
|
-
```
|
|
67
|
+
## API Reference
|
|
215
68
|
|
|
216
|
-
|
|
69
|
+
### `tw()` โ The Main Function
|
|
217
70
|
|
|
218
|
-
|
|
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 |
|
|
71
|
+
One function, four modes:
|
|
225
72
|
|
|
226
|
-
|
|
73
|
+
#### Mode 1: String โ Atomic Classes
|
|
227
74
|
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
|
|
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
|
|
231
79
|
```
|
|
232
80
|
|
|
233
|
-
####
|
|
234
|
-
|
|
235
|
-
For CSS that Tailwind can't express, use the `@css` directive:
|
|
236
|
-
|
|
237
|
-
**String form:**
|
|
81
|
+
#### Mode 2: Named Class
|
|
238
82
|
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
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
|
-
})
|
|
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
|
|
260
87
|
```
|
|
261
88
|
|
|
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.
|
|
267
|
-
|
|
268
|
-
```javascript
|
|
269
|
-
import { twsxVariants } from 'tailwind-to-style'
|
|
89
|
+
#### Mode 3: Variants
|
|
270
90
|
|
|
271
|
-
|
|
272
|
-
|
|
91
|
+
```js
|
|
92
|
+
const button = tw({
|
|
93
|
+
name: 'btn',
|
|
94
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-all',
|
|
273
95
|
variants: {
|
|
274
|
-
variant: {
|
|
275
|
-
solid: 'shadow-sm',
|
|
276
|
-
outline: 'bg-transparent border-2',
|
|
277
|
-
ghost: 'bg-transparent border-transparent',
|
|
278
|
-
},
|
|
279
96
|
color: {
|
|
280
|
-
primary: 'bg-blue-
|
|
281
|
-
danger: 'bg-red-
|
|
282
|
-
|
|
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',
|
|
283
100
|
},
|
|
284
101
|
size: {
|
|
285
|
-
sm: 'px-3 py-1.5
|
|
286
|
-
md: 'px-4 py-2
|
|
287
|
-
lg: 'px-6 py-3
|
|
288
|
-
},
|
|
289
|
-
disabled: {
|
|
290
|
-
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',
|
|
291
105
|
},
|
|
292
106
|
},
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
{ variant: 'outline', color: 'danger', class: 'bg-transparent text-red-600 border-red-500' },
|
|
296
|
-
],
|
|
297
|
-
defaultVariants: { variant: 'solid', color: 'primary', size: 'md' },
|
|
298
|
-
})
|
|
107
|
+
defaultVariants: { color: 'primary', size: 'md' },
|
|
108
|
+
});
|
|
299
109
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
btn({ color: 'danger' }) // "btn btn-danger"
|
|
303
|
-
btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
|
|
110
|
+
button({ color: 'danger', size: 'lg' })
|
|
111
|
+
// โ "btn btn--color-danger btn--size-lg"
|
|
304
112
|
```
|
|
305
113
|
|
|
306
|
-
|
|
114
|
+
#### Mode 4: Slots (Multi-Part Components)
|
|
307
115
|
|
|
308
|
-
```
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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',
|
|
316
124
|
},
|
|
317
|
-
|
|
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
|
-
```
|
|
125
|
+
});
|
|
326
126
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
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:**
|
|
127
|
+
card()
|
|
128
|
+
// โ { root: "card__root", header: "card__header", body: "card__body", footer: "card__footer" }
|
|
129
|
+
```
|
|
336
130
|
|
|
337
|
-
|
|
338
|
-
import { twsxVariants, type VariantProps } from 'tailwind-to-style'
|
|
131
|
+
#### Utility Methods
|
|
339
132
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
|
|
345
|
-
},
|
|
346
|
-
defaultVariants: { variant: 'solid', size: 'md' },
|
|
347
|
-
})
|
|
348
|
-
|
|
349
|
-
type ButtonProps = VariantProps<typeof button>
|
|
350
|
-
// โ { variant?: 'solid' | 'outline', size?: 'sm' | 'md' | 'lg' }
|
|
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
|
|
351
137
|
```
|
|
352
138
|
|
|
353
139
|
---
|
|
354
140
|
|
|
355
|
-
### `
|
|
356
|
-
|
|
357
|
-
A built-in utility for conditionally joining class names โ replaces `clsx`/`classnames`:
|
|
141
|
+
### `tws()` โ Inline Styles
|
|
358
142
|
|
|
359
|
-
|
|
360
|
-
import { cx } from 'tailwind-to-style'
|
|
143
|
+
Convert Tailwind classes directly to CSS styles. No DOM injection needed.
|
|
361
144
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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;"
|
|
365
149
|
|
|
366
|
-
//
|
|
367
|
-
|
|
368
|
-
// โ '
|
|
150
|
+
// Returns JSON object (for React style prop, etc.)
|
|
151
|
+
tws('flex items-center gap-4', true)
|
|
152
|
+
// โ { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
153
|
+
```
|
|
369
154
|
|
|
370
|
-
|
|
371
|
-
cx('p-4', { 'bg-blue-500': isActive, 'opacity-50': isDisabled })
|
|
372
|
-
// โ 'p-4 bg-blue-500'
|
|
155
|
+
---
|
|
373
156
|
|
|
374
|
-
|
|
375
|
-
cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
|
|
376
|
-
// โ 'p-4 bg-white ring-2 ring-blue-500'
|
|
157
|
+
### `cx()` โ Conditional Class Names
|
|
377
158
|
|
|
378
|
-
|
|
379
|
-
const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))
|
|
380
|
-
```
|
|
159
|
+
A lightweight `clsx` alternative built-in.
|
|
381
160
|
|
|
382
|
-
|
|
161
|
+
```js
|
|
162
|
+
cx('base-class', isActive && 'active-class', { 'disabled': isDisabled })
|
|
163
|
+
// โ "base-class active-class"
|
|
383
164
|
|
|
384
|
-
|
|
385
|
-
|
|
165
|
+
// Arrays work too
|
|
166
|
+
cx(['p-4', 'bg-white'], condition && ['ring-2', 'ring-blue-500'])
|
|
386
167
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
btnClass({ 'opacity-50': disabled })
|
|
391
|
-
// โ 'px-4 py-2 rounded font-medium transition-colors opacity-50'
|
|
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"
|
|
392
171
|
```
|
|
393
172
|
|
|
394
173
|
---
|
|
395
174
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
The complete CSS-in-JS solution with automatic mode detection. Generates scoped class names with auto-injected CSS from Tailwind classes.
|
|
175
|
+
## React Bindings
|
|
399
176
|
|
|
400
|
-
```
|
|
401
|
-
import {
|
|
177
|
+
```bash
|
|
178
|
+
import { styled, ThemeProvider, useTheme, useTws } from 'tailwind-to-style/react';
|
|
402
179
|
```
|
|
403
180
|
|
|
404
|
-
|
|
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
|
-
```
|
|
181
|
+
### `styled()` โ Create Styled Components
|
|
420
182
|
|
|
421
|
-
|
|
183
|
+
```jsx
|
|
184
|
+
import { styled } from 'tailwind-to-style/react';
|
|
422
185
|
|
|
423
|
-
|
|
424
|
-
const button = twsxClassName({
|
|
186
|
+
const Button = styled('button', {
|
|
425
187
|
name: 'btn',
|
|
426
|
-
base: 'px-4 py-2 font-medium
|
|
188
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-colors',
|
|
427
189
|
variants: {
|
|
428
|
-
|
|
429
|
-
primary: 'bg-blue-
|
|
430
|
-
secondary: 'bg-gray-200 text-gray-
|
|
431
|
-
outline: 'border-2 border-blue-500 text-blue-500 hover:bg-blue-50',
|
|
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',
|
|
432
193
|
},
|
|
433
194
|
size: {
|
|
434
195
|
sm: 'text-sm px-3 py-1.5',
|
|
435
|
-
md: 'text-base px-4 py-2',
|
|
436
196
|
lg: 'text-lg px-6 py-3',
|
|
437
197
|
},
|
|
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
198
|
},
|
|
451
|
-
}
|
|
199
|
+
defaultVariants: { color: 'primary', size: 'sm' },
|
|
200
|
+
});
|
|
452
201
|
|
|
453
|
-
//
|
|
454
|
-
<
|
|
455
|
-
|
|
456
|
-
|
|
202
|
+
// Variant props are type-safe and stripped from DOM
|
|
203
|
+
<Button color="primary" size="lg" onClick={handleClick}>
|
|
204
|
+
Click Me
|
|
205
|
+
</Button>
|
|
457
206
|
```
|
|
458
207
|
|
|
459
|
-
|
|
208
|
+
### `ThemeProvider` & `useTheme`
|
|
460
209
|
|
|
461
|
-
```
|
|
462
|
-
|
|
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
|
-
```
|
|
210
|
+
```jsx
|
|
211
|
+
import { ThemeProvider, useTheme } from 'tailwind-to-style/react';
|
|
489
212
|
|
|
490
|
-
|
|
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({
|
|
213
|
+
const theme = {
|
|
530
214
|
colors: { primary: '#3b82f6', danger: '#ef4444' },
|
|
531
|
-
|
|
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 })
|
|
215
|
+
radius: { sm: '0.25rem', md: '0.5rem' },
|
|
216
|
+
};
|
|
573
217
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
duration: '300ms',
|
|
582
|
-
timing: 'ease-out',
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
// Get all registered animations
|
|
586
|
-
twsxClassName.getAnimations() // โ { myFade: {...}, ... }
|
|
587
|
-
|
|
588
|
-
// Use in config
|
|
589
|
-
const modal = twsxClassName({
|
|
590
|
-
_: 'fixed inset-0',
|
|
591
|
-
animation: 'myFade', // or built-in: 'fadeIn', 'slideUp', etc.
|
|
592
|
-
})
|
|
593
|
-
|
|
594
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
595
|
-
// SSR Support
|
|
596
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
597
|
-
|
|
598
|
-
// Get CSS for specific className
|
|
599
|
-
twsxClassName.getCSS('btn-a1b2c3d4')
|
|
600
|
-
|
|
601
|
-
// Get all generated CSS
|
|
602
|
-
twsxClassName.getAllCSS()
|
|
603
|
-
|
|
604
|
-
// Extract as style tag (for SSR)
|
|
605
|
-
const styleTag = twsxClassName.extractCSS()
|
|
606
|
-
// โ '<style data-twsx-classname>...</style>'
|
|
607
|
-
|
|
608
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
609
|
-
// Cache Management
|
|
610
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
611
|
-
|
|
612
|
-
// Clear all caches
|
|
613
|
-
twsxClassName.clearCache()
|
|
218
|
+
function App() {
|
|
219
|
+
return (
|
|
220
|
+
<ThemeProvider theme={theme}>
|
|
221
|
+
<MyComponent />
|
|
222
|
+
</ThemeProvider>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
614
225
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
//
|
|
226
|
+
function MyComponent() {
|
|
227
|
+
const { theme, setTheme } = useTheme();
|
|
228
|
+
// Access tokens via CSS variables: var(--tws-colors-primary)
|
|
229
|
+
}
|
|
618
230
|
```
|
|
619
231
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
### `tw()` โ Atomic CSS Classes
|
|
232
|
+
### `useTws()` โ Inline Style Hook
|
|
623
233
|
|
|
624
|
-
|
|
234
|
+
```jsx
|
|
235
|
+
import { useTws } from 'tailwind-to-style/react';
|
|
625
236
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
237
|
+
function Box({ classes }) {
|
|
238
|
+
const style = useTws(classes); // memoized style object
|
|
239
|
+
return <div style={style}>Content</div>;
|
|
240
|
+
}
|
|
629
241
|
```
|
|
630
242
|
|
|
631
|
-
|
|
243
|
+
---
|
|
632
244
|
|
|
633
|
-
|
|
634
|
-
// Returns: "tw-flex tw-gap-3 tw-items-center"
|
|
635
|
-
tw('flex gap-3 items-center')
|
|
245
|
+
## Design Tokens
|
|
636
246
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<span>Item 1</span>
|
|
640
|
-
<span>Item 2</span>
|
|
641
|
-
</div>
|
|
247
|
+
```js
|
|
248
|
+
import { createTheme, tokenRegistry, token } from 'tailwind-to-style/tokens';
|
|
642
249
|
```
|
|
643
250
|
|
|
644
|
-
|
|
251
|
+
### `createTheme()`
|
|
645
252
|
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
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
|
+
// ...
|
|
654
267
|
```
|
|
655
268
|
|
|
656
|
-
|
|
269
|
+
### `tokenRegistry`
|
|
657
270
|
|
|
658
|
-
```
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
<!-- Responsive grid -->
|
|
664
|
-
</div>
|
|
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 */ })
|
|
665
276
|
```
|
|
666
277
|
|
|
667
|
-
|
|
278
|
+
### `token()` โ CSS Variable Reference
|
|
668
279
|
|
|
669
|
-
```
|
|
670
|
-
|
|
671
|
-
|
|
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>
|
|
280
|
+
```js
|
|
281
|
+
token('colors.primary') // โ "var(--tws-colors-primary)"
|
|
282
|
+
token('colors.primary', '#000') // โ "var(--tws-colors-primary, #000)"
|
|
681
283
|
```
|
|
682
284
|
|
|
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
285
|
---
|
|
692
286
|
|
|
693
|
-
##
|
|
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
|
-
})
|
|
287
|
+
## Animations
|
|
725
288
|
|
|
726
|
-
|
|
727
|
-
|
|
289
|
+
```js
|
|
290
|
+
import { animate, defineAnimation, getAnimationNames } from 'tailwind-to-style/animations';
|
|
728
291
|
```
|
|
729
292
|
|
|
730
|
-
|
|
293
|
+
### Built-in Presets
|
|
731
294
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
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
|
|
300
|
+
```
|
|
738
301
|
|
|
739
|
-
|
|
302
|
+
Available presets: `fadeIn`, `fadeOut`, `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `scaleIn`, `scaleOut`, `bounce`, `shake`, `pulse`, `spin`, `ping`
|
|
740
303
|
|
|
741
|
-
|
|
304
|
+
### Custom Animations
|
|
742
305
|
|
|
743
|
-
```
|
|
744
|
-
|
|
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
|
+
});
|
|
745
317
|
|
|
746
|
-
//
|
|
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')
|
|
318
|
+
animate('wiggle'); // works!
|
|
769
319
|
```
|
|
770
320
|
|
|
771
321
|
---
|
|
772
322
|
|
|
773
323
|
## SSR (Server-Side Rendering)
|
|
774
324
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
```javascript
|
|
778
|
-
import { startSSR, stopSSR, getSSRStyles, twsx } from 'tailwind-to-style'
|
|
779
|
-
|
|
780
|
-
// 1. Start collecting
|
|
781
|
-
startSSR()
|
|
325
|
+
```js
|
|
326
|
+
import { tw, createSSRCollector } from 'tailwind-to-style';
|
|
782
327
|
|
|
783
|
-
//
|
|
784
|
-
|
|
785
|
-
twsx({ '.btn': 'bg-blue-500 text-white px-4 py-2 rounded' })
|
|
786
|
-
const html = renderToString(<App />)
|
|
328
|
+
// Collect all CSS generated during render
|
|
329
|
+
const collector = createSSRCollector();
|
|
787
330
|
|
|
788
|
-
|
|
789
|
-
const css =
|
|
331
|
+
const html = renderToString(<App />);
|
|
332
|
+
const css = collector.extract();
|
|
790
333
|
|
|
791
|
-
//
|
|
334
|
+
// Inject into HTML head
|
|
792
335
|
const fullHtml = `
|
|
793
336
|
<html>
|
|
794
337
|
<head><style>${css}</style></head>
|
|
795
338
|
<body>${html}</body>
|
|
796
339
|
</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
|
|
340
|
+
`;
|
|
958
341
|
```
|
|
959
342
|
|
|
960
343
|
---
|
|
961
344
|
|
|
962
345
|
## Tree-Shakeable Imports
|
|
963
346
|
|
|
964
|
-
Import only what you need
|
|
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
|
|
347
|
+
Import only what you need for minimal bundle size:
|
|
973
348
|
|
|
974
|
-
|
|
975
|
-
import { tws, twsx, twsxVariants, twsxClassName, cx } from 'tailwind-to-style' // ~15KB
|
|
976
|
-
```
|
|
977
|
-
|
|
978
|
-
| Import Path | Includes | Size (minified) |
|
|
349
|
+
| Import Path | What You Get | ~Size |
|
|
979
350
|
|---|---|---|
|
|
980
|
-
| `tailwind-to-style` |
|
|
981
|
-
| `tailwind-to-style/
|
|
982
|
-
| `tailwind-to-style/
|
|
983
|
-
| `tailwind-to-style/
|
|
984
|
-
| `tailwind-to-style/
|
|
985
|
-
| `tailwind-to-style/
|
|
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
|
-
```
|
|
1042
|
-
|
|
1043
|
-
---
|
|
1044
|
-
|
|
1045
|
-
## Preflight CSS
|
|
1046
|
-
|
|
1047
|
-
For best results, import Tailwind's preflight (base/reset styles):
|
|
1048
|
-
|
|
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
|
-
```
|
|
1057
|
-
|
|
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
|
-
```
|
|
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 |
|
|
1163
357
|
|
|
1164
358
|
---
|
|
1165
359
|
|
|
1166
|
-
##
|
|
1167
|
-
|
|
1168
|
-
v3.2.0 includes major performance optimizations:
|
|
360
|
+
## Framework Support
|
|
1169
361
|
|
|
1170
|
-
|
|
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
|
|
362
|
+
Works with any framework or vanilla JS:
|
|
1175
363
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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)
|
|
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
|
|
1275
369
|
|
|
1276
370
|
---
|
|
1277
371
|
|
|
1278
372
|
## License
|
|
1279
373
|
|
|
1280
374
|
MIT ยฉ [Bigetion](https://github.com/Bigetion)
|
|
1281
|
-
|
|
1282
|
-
---
|
|
1283
|
-
|
|
1284
|
-
**v3.2.0** โ [Changelog](CHANGELOG.md) ยท [Architecture](ARCHITECTURE.md) ยท [Migration Guide](MIGRATION.md)
|