tailwind-to-style 3.3.0 โ 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +222 -1109
- 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/llms.txt +481 -0
- package/package.json +38 -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,366 @@ 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
|
|
52
|
+
```js
|
|
53
|
+
import { tw, tws, cx } from 'tailwind-to-style';
|
|
132
54
|
|
|
133
|
-
|
|
55
|
+
// Generate atomic CSS classes (auto-injected into DOM)
|
|
56
|
+
document.body.className = tw('flex items-center justify-center min-h-screen bg-gray-100');
|
|
134
57
|
|
|
135
|
-
|
|
58
|
+
// Convert to inline styles
|
|
59
|
+
element.style.cssText = tws('bg-blue-500 text-white p-4 rounded-lg');
|
|
136
60
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
// CSS string (default)
|
|
141
|
-
tws('bg-blue-500 p-4 rounded-lg')
|
|
142
|
-
// โ "background-color: #3b82f6; padding: 1rem; border-radius: 0.5rem;"
|
|
143
|
-
|
|
144
|
-
// JSON object (pass `true` as 2nd argument)
|
|
145
|
-
tws('flex items-center gap-4', true)
|
|
146
|
-
// โ { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
61
|
+
// Conditional class merging
|
|
62
|
+
const classes = cx('base', isActive && 'ring-2', { 'opacity-50': disabled });
|
|
147
63
|
```
|
|
148
64
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```javascript
|
|
152
|
-
// Responsive breakpoints
|
|
153
|
-
tws('text-sm md:text-base lg:text-lg')
|
|
154
|
-
|
|
155
|
-
// Pseudo-states
|
|
156
|
-
tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
|
|
65
|
+
---
|
|
157
66
|
|
|
158
|
-
|
|
159
|
-
tws('w-[123px] text-[#abc] mt-[2.5rem] grid-cols-[1fr,2fr]')
|
|
67
|
+
## Examples
|
|
160
68
|
|
|
161
|
-
|
|
162
|
-
tws('!bg-red-500 !text-white')
|
|
69
|
+
Want to try the library with real demos?
|
|
163
70
|
|
|
164
|
-
|
|
165
|
-
|
|
71
|
+
- `examples/basic/` โ runtime `tws()` examples for inline conversion and custom values.
|
|
72
|
+
- `examples/react-demo/` โ full React showcase with components, variants, tokens, and theme switching.
|
|
73
|
+
- `examples/twsx-classname-app/` โ Vite-based runtime `tw()` v4 demo with variant and slots components.
|
|
166
74
|
|
|
167
|
-
|
|
168
|
-
tws('bg-blue-500/50 text-black/75')
|
|
75
|
+
Run the demos by opening `examples/README.md` or using the commands below:
|
|
169
76
|
|
|
170
|
-
|
|
171
|
-
|
|
77
|
+
```bash
|
|
78
|
+
cd examples/react-demo
|
|
79
|
+
npm install
|
|
80
|
+
npm run dev
|
|
172
81
|
```
|
|
173
82
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
<h1 style={tws('text-2xl font-bold text-gray-900', true)}>Hello</h1>
|
|
179
|
-
</div>
|
|
83
|
+
```bash
|
|
84
|
+
cd examples/twsx-classname-app
|
|
85
|
+
npm install
|
|
86
|
+
npm run dev
|
|
180
87
|
```
|
|
181
88
|
|
|
182
89
|
---
|
|
183
90
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Generates real CSS from Tailwind classes with full selector support, SCSS-like nesting, and auto-injects a `<style>` tag into the DOM.
|
|
187
|
-
|
|
188
|
-
> **HMR-safe** โ each `twsx()` call owns a stable slot in the injected style tag keyed by its top-level selectors. When you edit styles during development, the old slot is **replaced** (not appended), so changes are reflected immediately without a hard refresh.
|
|
189
|
-
|
|
190
|
-
```javascript
|
|
191
|
-
import { twsx } from 'tailwind-to-style'
|
|
192
|
-
|
|
193
|
-
twsx({
|
|
194
|
-
'.button': [
|
|
195
|
-
'bg-blue-500 text-white px-6 py-3 rounded-lg font-medium transition-all',
|
|
196
|
-
{
|
|
197
|
-
'&:hover': 'bg-blue-600 shadow-lg transform scale-105',
|
|
198
|
-
'&:active': 'bg-blue-700 scale-95',
|
|
199
|
-
'&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
|
|
200
|
-
'&.large': 'px-8 py-4 text-lg',
|
|
201
|
-
}
|
|
202
|
-
],
|
|
203
|
-
|
|
204
|
-
'.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
205
|
-
'.card > .header': 'p-6 border-b border-gray-200',
|
|
206
|
-
'.card > .body': 'p-6',
|
|
207
|
-
|
|
208
|
-
// Media queries
|
|
209
|
-
'@media (max-width: 768px)': {
|
|
210
|
-
'.card': 'rounded-lg',
|
|
211
|
-
'.card > .header': 'p-4',
|
|
212
|
-
}
|
|
213
|
-
})
|
|
214
|
-
```
|
|
91
|
+
## API Reference
|
|
215
92
|
|
|
216
|
-
|
|
93
|
+
### `tw()` โ The Main Function
|
|
217
94
|
|
|
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 |
|
|
95
|
+
One function, four modes:
|
|
225
96
|
|
|
226
|
-
|
|
97
|
+
#### Mode 1: String โ Atomic Classes
|
|
227
98
|
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
|
|
99
|
+
```js
|
|
100
|
+
tw('flex items-center gap-4 hover:bg-gray-100')
|
|
101
|
+
// โ "tw-flex tw-items-center tw-gap-4 tw-hover-bg-gray-100"
|
|
102
|
+
// CSS is auto-injected with full pseudo-class support
|
|
231
103
|
```
|
|
232
104
|
|
|
233
|
-
####
|
|
234
|
-
|
|
235
|
-
For CSS that Tailwind can't express, use the `@css` directive:
|
|
236
|
-
|
|
237
|
-
**String form:**
|
|
238
|
-
|
|
239
|
-
```javascript
|
|
240
|
-
twsx({
|
|
241
|
-
'.gradient-text': '@css { background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }',
|
|
242
|
-
})
|
|
243
|
-
```
|
|
105
|
+
#### Mode 2: Named Class
|
|
244
106
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
'.gradient-text': [
|
|
250
|
-
'text-3xl font-bold',
|
|
251
|
-
{
|
|
252
|
-
'@css': {
|
|
253
|
-
'background': 'linear-gradient(90deg, #ff6b6b, #feca57)',
|
|
254
|
-
'-webkit-background-clip': 'text',
|
|
255
|
-
'-webkit-text-fill-color': 'transparent',
|
|
256
|
-
},
|
|
257
|
-
},
|
|
258
|
-
],
|
|
259
|
-
})
|
|
107
|
+
```js
|
|
108
|
+
tw('sidebar', 'w-64 h-screen bg-white border-r border-gray-200')
|
|
109
|
+
// โ "sidebar"
|
|
110
|
+
// Generates .sidebar { ... } with all the Tailwind styles
|
|
260
111
|
```
|
|
261
112
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
### `twsxVariants()` โ Component Variant System
|
|
265
|
-
|
|
266
|
-
A CVA-like API for building type-safe component variants. Auto-generates CSS for all combinations and returns a class name builder function.
|
|
113
|
+
#### Mode 3: Variants
|
|
267
114
|
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
base: 'px-4 py-2 rounded-lg font-medium transition-all border',
|
|
115
|
+
```js
|
|
116
|
+
const button = tw({
|
|
117
|
+
name: 'btn',
|
|
118
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-all',
|
|
273
119
|
variants: {
|
|
274
|
-
variant: {
|
|
275
|
-
solid: 'shadow-sm',
|
|
276
|
-
outline: 'bg-transparent border-2',
|
|
277
|
-
ghost: 'bg-transparent border-transparent',
|
|
278
|
-
},
|
|
279
120
|
color: {
|
|
280
|
-
primary: 'bg-blue-
|
|
281
|
-
danger: 'bg-red-
|
|
282
|
-
|
|
121
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
122
|
+
danger: 'bg-red-600 text-white hover:bg-red-700',
|
|
123
|
+
ghost: 'bg-transparent text-gray-700 hover:bg-gray-100',
|
|
283
124
|
},
|
|
284
125
|
size: {
|
|
285
|
-
sm: 'px-3 py-1.5
|
|
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',
|
|
126
|
+
sm: 'text-sm px-3 py-1.5',
|
|
127
|
+
md: 'text-base px-4 py-2',
|
|
128
|
+
lg: 'text-lg px-6 py-3',
|
|
291
129
|
},
|
|
292
130
|
},
|
|
293
|
-
|
|
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
|
-
})
|
|
131
|
+
defaultVariants: { color: 'primary', size: 'md' },
|
|
132
|
+
});
|
|
299
133
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
btn({ color: 'danger' }) // "btn btn-danger"
|
|
303
|
-
btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
|
|
134
|
+
button({ color: 'danger', size: 'lg' })
|
|
135
|
+
// โ "btn btn--color-danger btn--size-lg"
|
|
304
136
|
```
|
|
305
137
|
|
|
306
|
-
|
|
138
|
+
#### Mode 4: Slots (Multi-Part Components)
|
|
307
139
|
|
|
308
|
-
```
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
140
|
+
```js
|
|
141
|
+
const card = tw({
|
|
142
|
+
name: 'card',
|
|
143
|
+
slots: {
|
|
144
|
+
root: 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
145
|
+
header: 'px-6 py-4 border-b border-gray-100',
|
|
146
|
+
body: 'px-6 py-4',
|
|
147
|
+
footer: 'px-6 py-4 bg-gray-50',
|
|
316
148
|
},
|
|
317
|
-
|
|
318
|
-
nested: {
|
|
319
|
-
'.alert-icon': 'flex-shrink-0 mt-0.5',
|
|
320
|
-
'.alert-content': 'flex-1',
|
|
321
|
-
'.alert-dismiss': 'p-1 rounded hover:bg-black/10 cursor-pointer',
|
|
322
|
-
}
|
|
323
|
-
})
|
|
324
|
-
// Generates: .alert .alert-icon { ... }, .alert .alert-content { ... }, etc.
|
|
325
|
-
```
|
|
149
|
+
});
|
|
326
150
|
|
|
327
|
-
|
|
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:**
|
|
336
|
-
|
|
337
|
-
```typescript
|
|
338
|
-
import { twsxVariants, type VariantProps } from 'tailwind-to-style'
|
|
151
|
+
card()
|
|
152
|
+
// โ { root: "card__root", header: "card__header", body: "card__body", footer: "card__footer" }
|
|
153
|
+
```
|
|
339
154
|
|
|
340
|
-
|
|
341
|
-
base: 'px-4 py-2 rounded',
|
|
342
|
-
variants: {
|
|
343
|
-
variant: { solid: 'bg-blue-500', outline: 'border-2' },
|
|
344
|
-
size: { sm: 'text-sm', md: 'text-base', lg: 'text-lg' },
|
|
345
|
-
},
|
|
346
|
-
defaultVariants: { variant: 'solid', size: 'md' },
|
|
347
|
-
})
|
|
155
|
+
#### Utility Methods
|
|
348
156
|
|
|
349
|
-
|
|
350
|
-
//
|
|
157
|
+
```js
|
|
158
|
+
tw.extractCSS() // Get all generated CSS as string (SSR)
|
|
159
|
+
tw.clearCache() // Clear internal caches
|
|
160
|
+
tw.config({ prefix: 'my', hash: false }) // Configure globally
|
|
351
161
|
```
|
|
352
162
|
|
|
353
163
|
---
|
|
354
164
|
|
|
355
|
-
### `
|
|
356
|
-
|
|
357
|
-
A built-in utility for conditionally joining class names โ replaces `clsx`/`classnames`:
|
|
165
|
+
### `tws()` โ Inline Styles
|
|
358
166
|
|
|
359
|
-
|
|
360
|
-
import { cx } from 'tailwind-to-style'
|
|
167
|
+
Convert Tailwind classes directly to CSS styles. No DOM injection needed.
|
|
361
168
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
// Conditionals
|
|
367
|
-
cx('p-4', isActive && 'bg-blue-500', isDisabled && 'opacity-50')
|
|
368
|
-
// โ 'p-4 bg-blue-500'
|
|
169
|
+
```js
|
|
170
|
+
// Returns CSS string
|
|
171
|
+
tws('bg-blue-500 p-4 rounded-lg')
|
|
172
|
+
// โ "background-color: rgb(59,130,246); padding: 1rem; border-radius: 0.5rem;"
|
|
369
173
|
|
|
370
|
-
//
|
|
371
|
-
|
|
372
|
-
// โ '
|
|
174
|
+
// Returns JSON object (for React style prop, etc.)
|
|
175
|
+
tws('flex items-center gap-4', true)
|
|
176
|
+
// โ { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
177
|
+
```
|
|
373
178
|
|
|
374
|
-
|
|
375
|
-
cx(['p-4', 'bg-white'], isActive && ['ring-2', 'ring-blue-500'])
|
|
376
|
-
// โ 'p-4 bg-white ring-2 ring-blue-500'
|
|
179
|
+
---
|
|
377
180
|
|
|
378
|
-
|
|
379
|
-
const styles = tws(cx('p-4', isLarge && 'p-8', { 'bg-blue-500': isPrimary }))
|
|
380
|
-
```
|
|
181
|
+
### `cx()` โ Conditional Class Names
|
|
381
182
|
|
|
382
|
-
|
|
183
|
+
A lightweight `clsx` alternative built-in.
|
|
383
184
|
|
|
384
|
-
```
|
|
385
|
-
|
|
185
|
+
```js
|
|
186
|
+
cx('base-class', isActive && 'active-class', { 'disabled': isDisabled })
|
|
187
|
+
// โ "base-class active-class"
|
|
386
188
|
|
|
387
|
-
|
|
388
|
-
|
|
189
|
+
// Arrays work too
|
|
190
|
+
cx(['p-4', 'bg-white'], condition && ['ring-2', 'ring-blue-500'])
|
|
389
191
|
|
|
390
|
-
|
|
391
|
-
|
|
192
|
+
// Create pre-filled cx
|
|
193
|
+
const btnClass = cx.with('px-4 py-2 rounded font-medium');
|
|
194
|
+
btnClass('bg-blue-500') // โ "px-4 py-2 rounded font-medium bg-blue-500"
|
|
392
195
|
```
|
|
393
196
|
|
|
394
197
|
---
|
|
395
198
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
The complete CSS-in-JS solution with automatic mode detection. Generates scoped class names with auto-injected CSS from Tailwind classes.
|
|
199
|
+
## React Bindings
|
|
399
200
|
|
|
400
|
-
```
|
|
401
|
-
import {
|
|
201
|
+
```bash
|
|
202
|
+
import { styled, ThemeProvider, useTheme, useTws } from 'tailwind-to-style/react';
|
|
402
203
|
```
|
|
403
204
|
|
|
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
|
-
```
|
|
205
|
+
### `styled()` โ Create Styled Components
|
|
420
206
|
|
|
421
|
-
|
|
207
|
+
```jsx
|
|
208
|
+
import { styled } from 'tailwind-to-style/react';
|
|
422
209
|
|
|
423
|
-
|
|
424
|
-
const button = twsxClassName({
|
|
210
|
+
const Button = styled('button', {
|
|
425
211
|
name: 'btn',
|
|
426
|
-
base: 'px-4 py-2 font-medium
|
|
212
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-colors',
|
|
427
213
|
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',
|
|
214
|
+
color: {
|
|
215
|
+
primary: 'bg-blue-600 text-white hover:bg-blue-700',
|
|
216
|
+
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
|
|
432
217
|
},
|
|
433
218
|
size: {
|
|
434
219
|
sm: 'text-sm px-3 py-1.5',
|
|
435
|
-
md: 'text-base px-4 py-2',
|
|
436
220
|
lg: 'text-lg px-6 py-3',
|
|
437
221
|
},
|
|
438
|
-
disabled: {
|
|
439
|
-
true: 'opacity-50 cursor-not-allowed',
|
|
440
|
-
false: 'cursor-pointer',
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
compoundVariants: [
|
|
444
|
-
{ variant: 'primary', size: 'lg', class: 'shadow-lg' },
|
|
445
|
-
],
|
|
446
|
-
defaultVariants: {
|
|
447
|
-
variant: 'primary',
|
|
448
|
-
size: 'md',
|
|
449
|
-
disabled: false,
|
|
450
222
|
},
|
|
451
|
-
}
|
|
223
|
+
defaultVariants: { color: 'primary', size: 'sm' },
|
|
224
|
+
});
|
|
452
225
|
|
|
453
|
-
//
|
|
454
|
-
<
|
|
455
|
-
|
|
456
|
-
|
|
226
|
+
// Variant props are type-safe and stripped from DOM
|
|
227
|
+
<Button color="primary" size="lg" onClick={handleClick}>
|
|
228
|
+
Click Me
|
|
229
|
+
</Button>
|
|
457
230
|
```
|
|
458
231
|
|
|
459
|
-
|
|
232
|
+
### `ThemeProvider` & `useTheme`
|
|
460
233
|
|
|
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
|
-
```
|
|
234
|
+
```jsx
|
|
235
|
+
import { ThemeProvider, useTheme } from 'tailwind-to-style/react';
|
|
489
236
|
|
|
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({
|
|
237
|
+
const theme = {
|
|
530
238
|
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 })
|
|
573
|
-
|
|
574
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
575
|
-
// Animations
|
|
576
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
577
|
-
|
|
578
|
-
// Define custom animation preset
|
|
579
|
-
twsxClassName.defineAnimation('myFade', {
|
|
580
|
-
keyframes: { '0%': { opacity: 0 }, '100%': { opacity: 1 } },
|
|
581
|
-
duration: '300ms',
|
|
582
|
-
timing: 'ease-out',
|
|
583
|
-
})
|
|
584
|
-
|
|
585
|
-
// Get all registered animations
|
|
586
|
-
twsxClassName.getAnimations() // โ { myFade: {...}, ... }
|
|
239
|
+
radius: { sm: '0.25rem', md: '0.5rem' },
|
|
240
|
+
};
|
|
587
241
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
// SSR Support
|
|
596
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
597
|
-
|
|
598
|
-
// Get CSS for specific className
|
|
599
|
-
twsxClassName.getCSS('btn-a1b2c3d4')
|
|
600
|
-
|
|
601
|
-
// Get all generated CSS
|
|
602
|
-
twsxClassName.getAllCSS()
|
|
603
|
-
|
|
604
|
-
// Extract as style tag (for SSR)
|
|
605
|
-
const styleTag = twsxClassName.extractCSS()
|
|
606
|
-
// โ '<style data-twsx-classname>...</style>'
|
|
607
|
-
|
|
608
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
609
|
-
// Cache Management
|
|
610
|
-
// โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
611
|
-
|
|
612
|
-
// Clear all caches
|
|
613
|
-
twsxClassName.clearCache()
|
|
242
|
+
function App() {
|
|
243
|
+
return (
|
|
244
|
+
<ThemeProvider theme={theme}>
|
|
245
|
+
<MyComponent />
|
|
246
|
+
</ThemeProvider>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
614
249
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
//
|
|
250
|
+
function MyComponent() {
|
|
251
|
+
const { theme, setTheme } = useTheme();
|
|
252
|
+
// Access tokens via CSS variables: var(--tws-colors-primary)
|
|
253
|
+
}
|
|
618
254
|
```
|
|
619
255
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
### `tw()` โ Atomic CSS Classes
|
|
256
|
+
### `useTws()` โ Inline Style Hook
|
|
623
257
|
|
|
624
|
-
|
|
258
|
+
```jsx
|
|
259
|
+
import { useTws } from 'tailwind-to-style/react';
|
|
625
260
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
261
|
+
function Box({ classes }) {
|
|
262
|
+
const style = useTws(classes); // memoized style object
|
|
263
|
+
return <div style={style}>Content</div>;
|
|
264
|
+
}
|
|
629
265
|
```
|
|
630
266
|
|
|
631
|
-
|
|
267
|
+
---
|
|
632
268
|
|
|
633
|
-
|
|
634
|
-
// Returns: "tw-flex tw-gap-3 tw-items-center"
|
|
635
|
-
tw('flex gap-3 items-center')
|
|
269
|
+
## Design Tokens
|
|
636
270
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
<span>Item 1</span>
|
|
640
|
-
<span>Item 2</span>
|
|
641
|
-
</div>
|
|
271
|
+
```js
|
|
272
|
+
import { createTheme, tokenRegistry, token } from 'tailwind-to-style/tokens';
|
|
642
273
|
```
|
|
643
274
|
|
|
644
|
-
|
|
275
|
+
### `createTheme()`
|
|
645
276
|
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
277
|
+
```js
|
|
278
|
+
createTheme({
|
|
279
|
+
colors: {
|
|
280
|
+
primary: '#3b82f6',
|
|
281
|
+
secondary: '#8b5cf6',
|
|
282
|
+
success: '#10b981',
|
|
283
|
+
},
|
|
284
|
+
spacing: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
|
|
285
|
+
radius: { sm: '0.25rem', md: '0.5rem', lg: '1rem' },
|
|
286
|
+
}, { selector: ':root' });
|
|
287
|
+
// Injects CSS variables on :root:
|
|
288
|
+
// --tws-colors-primary: #3b82f6;
|
|
289
|
+
// --tws-colors-secondary: #8b5cf6;
|
|
290
|
+
// ...
|
|
654
291
|
```
|
|
655
292
|
|
|
656
|
-
|
|
293
|
+
### `tokenRegistry`
|
|
657
294
|
|
|
658
|
-
```
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
<!-- Responsive grid -->
|
|
664
|
-
</div>
|
|
295
|
+
```js
|
|
296
|
+
tokenRegistry.get('colors.primary') // โ '#3b82f6'
|
|
297
|
+
tokenRegistry.set('colors.primary', '#2563eb')
|
|
298
|
+
tokenRegistry.toCSS() // โ full :root CSS string
|
|
299
|
+
tokenRegistry.subscribe((tokens) => { /* react to changes */ })
|
|
665
300
|
```
|
|
666
301
|
|
|
667
|
-
|
|
302
|
+
### `token()` โ CSS Variable Reference
|
|
668
303
|
|
|
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>
|
|
304
|
+
```js
|
|
305
|
+
token('colors.primary') // โ "var(--tws-colors-primary)"
|
|
306
|
+
token('colors.primary', '#000') // โ "var(--tws-colors-primary, #000)"
|
|
681
307
|
```
|
|
682
308
|
|
|
683
|
-
**When to use which:**
|
|
684
|
-
|
|
685
|
-
| Function | Use Case | Example |
|
|
686
|
-
|----------|----------|---------|
|
|
687
|
-
| `tws()` | Quick inline styles, no pseudo | `style="${tws('p-4 bg-blue')}"` |
|
|
688
|
-
| `tw()` | Reusable layout utilities | `class="${tw('flex gap-3 hover:...')}"` |
|
|
689
|
-
| `twsxClassName` | Components with variants | `class="${button({ size: 'lg' })}"` |
|
|
690
|
-
|
|
691
309
|
---
|
|
692
310
|
|
|
693
|
-
##
|
|
694
|
-
|
|
695
|
-
### `configure()` โ Custom Theme
|
|
696
|
-
|
|
697
|
-
Extend the default Tailwind theme with custom colors, spacing, fonts, and more.
|
|
698
|
-
|
|
699
|
-
```javascript
|
|
700
|
-
import { configure } from 'tailwind-to-style'
|
|
701
|
-
|
|
702
|
-
configure({
|
|
703
|
-
theme: {
|
|
704
|
-
extend: {
|
|
705
|
-
colors: {
|
|
706
|
-
brand: {
|
|
707
|
-
50: '#eff6ff',
|
|
708
|
-
100: '#dbeafe',
|
|
709
|
-
500: '#3b82f6',
|
|
710
|
-
600: '#2563eb',
|
|
711
|
-
900: '#1e3a8a',
|
|
712
|
-
},
|
|
713
|
-
accent: '#f59e0b',
|
|
714
|
-
},
|
|
715
|
-
spacing: {
|
|
716
|
-
'128': '32rem',
|
|
717
|
-
'144': '36rem',
|
|
718
|
-
},
|
|
719
|
-
fontFamily: {
|
|
720
|
-
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
721
|
-
},
|
|
722
|
-
},
|
|
723
|
-
},
|
|
724
|
-
})
|
|
311
|
+
## Animations
|
|
725
312
|
|
|
726
|
-
|
|
727
|
-
|
|
313
|
+
```js
|
|
314
|
+
import { animate, defineAnimation, getAnimationNames } from 'tailwind-to-style/animations';
|
|
728
315
|
```
|
|
729
316
|
|
|
730
|
-
|
|
317
|
+
### Built-in Presets
|
|
731
318
|
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
319
|
+
```js
|
|
320
|
+
element.className = animate('fadeIn');
|
|
321
|
+
element.className = animate('slideInUp', { duration: '500ms', delay: '100ms' });
|
|
322
|
+
element.className = animate('bounce');
|
|
323
|
+
element.className = animate('spin'); // infinite
|
|
324
|
+
```
|
|
738
325
|
|
|
739
|
-
|
|
326
|
+
Available presets: `fadeIn`, `fadeOut`, `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `scaleIn`, `scaleOut`, `bounce`, `shake`, `pulse`, `spin`, `ping`
|
|
740
327
|
|
|
741
|
-
|
|
328
|
+
### Custom Animations
|
|
742
329
|
|
|
743
|
-
```
|
|
744
|
-
|
|
330
|
+
```js
|
|
331
|
+
defineAnimation('wiggle', {
|
|
332
|
+
keyframes: [
|
|
333
|
+
{ transform: 'rotate(0deg)' },
|
|
334
|
+
{ transform: 'rotate(-3deg)' },
|
|
335
|
+
{ transform: 'rotate(3deg)' },
|
|
336
|
+
{ transform: 'rotate(0deg)' },
|
|
337
|
+
],
|
|
338
|
+
duration: '300ms',
|
|
339
|
+
easing: 'ease-in-out',
|
|
340
|
+
});
|
|
745
341
|
|
|
746
|
-
//
|
|
747
|
-
const textShadow = createPlugin('text-shadow', {
|
|
748
|
-
utilities: {
|
|
749
|
-
'text-shadow-sm': { textShadow: '0 1px 2px rgba(0,0,0,0.05)' },
|
|
750
|
-
'text-shadow-md': { textShadow: '0 2px 4px rgba(0,0,0,0.1)' },
|
|
751
|
-
'text-shadow-lg': { textShadow: '0 4px 8px rgba(0,0,0,0.15)' },
|
|
752
|
-
},
|
|
753
|
-
})
|
|
754
|
-
|
|
755
|
-
// Dynamic plugin โ value-based utilities
|
|
756
|
-
const glass = createUtilityPlugin('glass', {
|
|
757
|
-
prefix: 'glass',
|
|
758
|
-
values: { sm: '4px', md: '8px', lg: '16px' },
|
|
759
|
-
formatter: (value) => ({
|
|
760
|
-
backdropFilter: `blur(${value})`,
|
|
761
|
-
backgroundColor: 'rgba(255,255,255,0.1)',
|
|
762
|
-
}),
|
|
763
|
-
})
|
|
764
|
-
|
|
765
|
-
configure({ plugins: [textShadow, glass] })
|
|
766
|
-
|
|
767
|
-
// Now use custom utilities
|
|
768
|
-
tws('text-shadow-md glass-lg')
|
|
342
|
+
animate('wiggle'); // works!
|
|
769
343
|
```
|
|
770
344
|
|
|
771
345
|
---
|
|
772
346
|
|
|
773
347
|
## SSR (Server-Side Rendering)
|
|
774
348
|
|
|
775
|
-
|
|
349
|
+
```js
|
|
350
|
+
import { tw, createSSRCollector } from 'tailwind-to-style';
|
|
776
351
|
|
|
777
|
-
|
|
778
|
-
|
|
352
|
+
// Collect all CSS generated during render
|
|
353
|
+
const collector = createSSRCollector();
|
|
779
354
|
|
|
780
|
-
|
|
781
|
-
|
|
355
|
+
const html = renderToString(<App />);
|
|
356
|
+
const css = collector.extract();
|
|
782
357
|
|
|
783
|
-
//
|
|
784
|
-
twsx({ '.card': 'bg-white p-6 rounded-lg shadow-md' })
|
|
785
|
-
twsx({ '.btn': 'bg-blue-500 text-white px-4 py-2 rounded' })
|
|
786
|
-
const html = renderToString(<App />)
|
|
787
|
-
|
|
788
|
-
// 3. Get collected CSS
|
|
789
|
-
const css = stopSSR()
|
|
790
|
-
|
|
791
|
-
// 4. Inject into HTML response
|
|
358
|
+
// Inject into HTML head
|
|
792
359
|
const fullHtml = `
|
|
793
360
|
<html>
|
|
794
361
|
<head><style>${css}</style></head>
|
|
795
362
|
<body>${html}</body>
|
|
796
363
|
</html>
|
|
797
|
-
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
**SSR API:**
|
|
801
|
-
|
|
802
|
-
| Function | Description |
|
|
803
|
-
|---|---|
|
|
804
|
-
| `startSSR()` | Begin collecting CSS |
|
|
805
|
-
| `stopSSR()` | Stop collecting, return all CSS as string |
|
|
806
|
-
| `getSSRStyles()` | Peek at collected CSS without stopping |
|
|
807
|
-
| `IS_BROWSER` | `true` in browser environment |
|
|
808
|
-
| `IS_SERVER` | `true` in Node.js/server environment |
|
|
809
|
-
|
|
810
|
-
### Advanced SSR Collector
|
|
811
|
-
|
|
812
|
-
For more control over SSR CSS collection:
|
|
813
|
-
|
|
814
|
-
```javascript
|
|
815
|
-
import { createSSRCollector } from 'tailwind-to-style'
|
|
816
|
-
|
|
817
|
-
// Create collector with options
|
|
818
|
-
const ssr = createSSRCollector({
|
|
819
|
-
dedupe: true, // Remove duplicate rules
|
|
820
|
-
minify: true, // Minify output CSS
|
|
821
|
-
sort: true, // Sort by specificity
|
|
822
|
-
})
|
|
823
|
-
|
|
824
|
-
// Render app
|
|
825
|
-
const html = renderToString(<App />)
|
|
826
|
-
|
|
827
|
-
// Extract CSS
|
|
828
|
-
const css = ssr.extractRaw() // Raw CSS string
|
|
829
|
-
const styleTag = ssr.extract({ id: 'ssr-css' }) // <style id="ssr-css">...</style>
|
|
830
|
-
|
|
831
|
-
// Or extract critical CSS (above-the-fold)
|
|
832
|
-
const { critical, rest, stats } = ssr.extractCritical({ maxSize: 14000 })
|
|
833
|
-
// critical: CSS under 14KB limit
|
|
834
|
-
// rest: remaining CSS to lazy-load
|
|
835
|
-
// stats: { criticalSize, criticalCount, totalCount }
|
|
836
|
-
|
|
837
|
-
// Utilities
|
|
838
|
-
ssr.peek() // Preview CSS without stopping
|
|
839
|
-
ssr.count // Number of rules collected
|
|
840
|
-
ssr.uniqueCount // Unique rules (after dedupe)
|
|
841
|
-
ssr.clear(true) // Clear and restart collecting
|
|
842
|
-
ssr.getStats() // { ruleCount, uniqueCount, totalSize, minifiedSize }
|
|
843
|
-
```
|
|
844
|
-
|
|
845
|
-
---
|
|
846
|
-
|
|
847
|
-
## Animation System
|
|
848
|
-
|
|
849
|
-
### Built-in CSS Animations
|
|
850
|
-
|
|
851
|
-
Tailwind animation utilities work out of the box:
|
|
852
|
-
|
|
853
|
-
```javascript
|
|
854
|
-
tws('animate-spin') // โ animation: spin 1s linear infinite
|
|
855
|
-
tws('animate-bounce') // โ animation: bounce 1s infinite
|
|
856
|
-
tws('animate-pulse') // โ animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite
|
|
857
|
-
tws('animate-ping') // โ animation: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite
|
|
858
|
-
```
|
|
859
|
-
|
|
860
|
-
### Programmatic Animations (Web Animations API)
|
|
861
|
-
|
|
862
|
-
For interactive animations with full control:
|
|
863
|
-
|
|
864
|
-
```javascript
|
|
865
|
-
import { animate, chain, stagger, parallel, transition } from 'tailwind-to-style'
|
|
866
|
-
|
|
867
|
-
// Single element animation
|
|
868
|
-
const ctrl = animate(element, 'fadeIn', { duration: 300 })
|
|
869
|
-
ctrl.pause() // Pause
|
|
870
|
-
ctrl.play() // Resume
|
|
871
|
-
ctrl.reverse() // Reverse
|
|
872
|
-
ctrl.cancel() // Cancel
|
|
873
|
-
await ctrl.finished // Wait for completion
|
|
874
|
-
|
|
875
|
-
// Sequential animations
|
|
876
|
-
await chain(element, ['fadeIn', 'slideUp', 'pulse'])
|
|
877
|
-
|
|
878
|
-
// With custom options per step
|
|
879
|
-
await chain(element, [
|
|
880
|
-
'fadeIn',
|
|
881
|
-
{ name: 'slideUp', delay: 200 },
|
|
882
|
-
{ name: 'pulse', options: { iterations: 2 } }
|
|
883
|
-
])
|
|
884
|
-
|
|
885
|
-
// Staggered animations (lists, grids)
|
|
886
|
-
stagger('.card', 'fadeIn', {
|
|
887
|
-
delay: 50, // 50ms between each
|
|
888
|
-
from: 'start', // 'start' | 'end' | 'center' | 'random'
|
|
889
|
-
onAllComplete: () => console.log('Done!')
|
|
890
|
-
})
|
|
891
|
-
|
|
892
|
-
// Parallel animations on multiple elements
|
|
893
|
-
await parallel([el1, el2, el3], 'fadeIn')
|
|
894
|
-
|
|
895
|
-
// Transition between states
|
|
896
|
-
transition(element,
|
|
897
|
-
{ opacity: 0, transform: 'scale(0.9)' }, // from
|
|
898
|
-
{ opacity: 1, transform: 'scale(1)' }, // to
|
|
899
|
-
{ duration: 300, easing: 'ease-out' }
|
|
900
|
-
)
|
|
901
|
-
```
|
|
902
|
-
|
|
903
|
-
### Animation Presets
|
|
904
|
-
|
|
905
|
-
```javascript
|
|
906
|
-
import { ANIMATION_PRESETS, EASING } from 'tailwind-to-style'
|
|
907
|
-
|
|
908
|
-
// Available presets
|
|
909
|
-
// fadeIn, fadeOut, slideUp, slideDown, slideLeft, slideRight,
|
|
910
|
-
// zoomIn, zoomOut, bounce, shake, pulse, spin, flipX, flipY,
|
|
911
|
-
// enterScale, exitScale, wiggle, heartbeat
|
|
912
|
-
|
|
913
|
-
// Easing presets
|
|
914
|
-
// linear, ease, easeIn, easeOut, easeInOut,
|
|
915
|
-
// spring, springLight, springMedium, springHeavy,
|
|
916
|
-
// smooth, smoothIn, smoothOut, bounce, elastic
|
|
917
|
-
```
|
|
918
|
-
|
|
919
|
-
### Custom Keyframes
|
|
920
|
-
|
|
921
|
-
```javascript
|
|
922
|
-
import { createKeyframes, clearKeyframes, registerPreset } from 'tailwind-to-style'
|
|
923
|
-
|
|
924
|
-
// Create custom keyframes (auto-injects CSS)
|
|
925
|
-
const animationValue = createKeyframes('myFade', {
|
|
926
|
-
'0%': { opacity: 0, transform: 'translateY(-10px)' },
|
|
927
|
-
'100%': { opacity: 1, transform: 'translateY(0)' }
|
|
928
|
-
}, { duration: 400, easing: 'ease-out' })
|
|
929
|
-
// โ "myFade 400ms ease-out forwards"
|
|
930
|
-
|
|
931
|
-
// Register as reusable preset
|
|
932
|
-
registerPreset('myFade', {
|
|
933
|
-
keyframes: [{ opacity: 0 }, { opacity: 1 }],
|
|
934
|
-
options: { duration: 400 }
|
|
935
|
-
})
|
|
936
|
-
|
|
937
|
-
// Use it
|
|
938
|
-
animate(element, 'myFade')
|
|
939
|
-
|
|
940
|
-
// Cleanup
|
|
941
|
-
clearKeyframes()
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
### Animation Utilities
|
|
945
|
-
|
|
946
|
-
```javascript
|
|
947
|
-
import {
|
|
948
|
-
cancelAllAnimations,
|
|
949
|
-
getActiveAnimationCount,
|
|
950
|
-
isAnimationSupported,
|
|
951
|
-
getPresetNames
|
|
952
|
-
} from 'tailwind-to-style'
|
|
953
|
-
|
|
954
|
-
cancelAllAnimations() // Stop all running animations
|
|
955
|
-
getActiveAnimationCount() // Number of active animations
|
|
956
|
-
isAnimationSupported() // Check Web Animations API support
|
|
957
|
-
getPresetNames() // List all available preset names
|
|
364
|
+
`;
|
|
958
365
|
```
|
|
959
366
|
|
|
960
367
|
---
|
|
961
368
|
|
|
962
369
|
## Tree-Shakeable Imports
|
|
963
370
|
|
|
964
|
-
Import only what you need
|
|
965
|
-
|
|
966
|
-
```javascript
|
|
967
|
-
// Individual imports (recommended for production)
|
|
968
|
-
import { tws } from 'tailwind-to-style/tws' // ~3KB
|
|
969
|
-
import { twsx } from 'tailwind-to-style/twsx' // ~6KB
|
|
970
|
-
import { twsxVariants } from 'tailwind-to-style/twsx-variants' // ~6KB
|
|
971
|
-
import { twsxClassName, tw } from 'tailwind-to-style/classname' // ~8KB
|
|
972
|
-
import { cx } from 'tailwind-to-style/cx' // <1KB
|
|
973
|
-
|
|
974
|
-
// Full import (everything)
|
|
975
|
-
import { tws, twsx, twsxVariants, twsxClassName, cx } from 'tailwind-to-style' // ~15KB
|
|
976
|
-
```
|
|
371
|
+
Import only what you need for minimal bundle size:
|
|
977
372
|
|
|
978
|
-
| Import Path |
|
|
373
|
+
| Import Path | What You Get | ~Size |
|
|
979
374
|
|---|---|---|
|
|
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
|
-
```
|
|
375
|
+
| `tailwind-to-style` | `tw`, `tws`, `cx` | Full engine |
|
|
376
|
+
| `tailwind-to-style/react` | `styled`, `ThemeProvider`, `useTheme`, `useTws` | +2KB |
|
|
377
|
+
| `tailwind-to-style/tokens` | `createTheme`, `tokenRegistry`, `token` | +1KB |
|
|
378
|
+
| `tailwind-to-style/animations` | `animate`, `defineAnimation` | +1.5KB |
|
|
379
|
+
| `tailwind-to-style/cx` | `cx` only | ~300B |
|
|
380
|
+
| `tailwind-to-style/tws` | `tws` only | Subset |
|
|
1042
381
|
|
|
1043
382
|
---
|
|
1044
383
|
|
|
1045
|
-
##
|
|
1046
|
-
|
|
1047
|
-
For best results, import Tailwind's preflight (base/reset styles):
|
|
384
|
+
## Framework Support
|
|
1048
385
|
|
|
1049
|
-
|
|
1050
|
-
import 'tailwind-to-style/preflight.css'
|
|
1051
|
-
```
|
|
1052
|
-
|
|
1053
|
-
```html
|
|
1054
|
-
<!-- Or in HTML -->
|
|
1055
|
-
<link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">
|
|
1056
|
-
```
|
|
386
|
+
Works with any framework or vanilla JS:
|
|
1057
387
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
## Framework Integration
|
|
1063
|
-
|
|
1064
|
-
### React
|
|
1065
|
-
|
|
1066
|
-
```jsx
|
|
1067
|
-
import { tws, twsx } from 'tailwind-to-style'
|
|
1068
|
-
import { useEffect } from 'react'
|
|
1069
|
-
|
|
1070
|
-
function App() {
|
|
1071
|
-
// Inject CSS on mount
|
|
1072
|
-
useEffect(() => {
|
|
1073
|
-
twsx({
|
|
1074
|
-
'.card': ['bg-white rounded-xl shadow-md p-6', {
|
|
1075
|
-
'&:hover': 'shadow-xl',
|
|
1076
|
-
'> .title': 'text-xl font-bold',
|
|
1077
|
-
}]
|
|
1078
|
-
})
|
|
1079
|
-
}, [])
|
|
1080
|
-
|
|
1081
|
-
return (
|
|
1082
|
-
<div style={tws('flex items-center gap-4', true)}>
|
|
1083
|
-
<button style={tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)}>
|
|
1084
|
-
Click me
|
|
1085
|
-
</button>
|
|
1086
|
-
</div>
|
|
1087
|
-
)
|
|
1088
|
-
}
|
|
1089
|
-
```
|
|
1090
|
-
|
|
1091
|
-
### Vue
|
|
1092
|
-
|
|
1093
|
-
```vue
|
|
1094
|
-
<script setup>
|
|
1095
|
-
import 'tailwind-to-style/preflight.css'
|
|
1096
|
-
import { tws, twsx } from 'tailwind-to-style'
|
|
1097
|
-
|
|
1098
|
-
// twsx() at the top level of <script setup> is HMR-safe.
|
|
1099
|
-
// When you edit the classes, Vite's HMR re-runs this block and
|
|
1100
|
-
// the old CSS slot is replaced automatically โ no hard refresh needed.
|
|
1101
|
-
twsx({
|
|
1102
|
-
'html': 'bg-gray-100 min-h-screen flex items-center justify-center',
|
|
1103
|
-
'.card': [
|
|
1104
|
-
'bg-white p-5 border border-gray-300 rounded-xl shadow-md',
|
|
1105
|
-
{
|
|
1106
|
-
'&:hover': 'shadow-xl',
|
|
1107
|
-
'> .title': 'text-xl font-bold text-gray-900 mb-3',
|
|
1108
|
-
'> .body': 'text-sm text-gray-700',
|
|
1109
|
-
},
|
|
1110
|
-
],
|
|
1111
|
-
})
|
|
1112
|
-
</script>
|
|
1113
|
-
|
|
1114
|
-
<template>
|
|
1115
|
-
<div class="card">
|
|
1116
|
-
<div class="title">Card Title</div>
|
|
1117
|
-
<p class="body">Styled with twsx โ hot reload works out of the box.</p>
|
|
1118
|
-
</div>
|
|
1119
|
-
</template>
|
|
1120
|
-
```
|
|
1121
|
-
|
|
1122
|
-
For simple inline styles, use `tws()` with the reactive system:
|
|
1123
|
-
|
|
1124
|
-
```vue
|
|
1125
|
-
<script setup>
|
|
1126
|
-
import { tws } from 'tailwind-to-style'
|
|
1127
|
-
const btnStyle = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
|
|
1128
|
-
</script>
|
|
1129
|
-
|
|
1130
|
-
<template>
|
|
1131
|
-
<button :style="btnStyle">Click me</button>
|
|
1132
|
-
</template>
|
|
1133
|
-
```
|
|
1134
|
-
|
|
1135
|
-
### Svelte
|
|
1136
|
-
|
|
1137
|
-
```svelte
|
|
1138
|
-
<script>
|
|
1139
|
-
import { tws } from 'tailwind-to-style'
|
|
1140
|
-
const style = tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true)
|
|
1141
|
-
</script>
|
|
1142
|
-
|
|
1143
|
-
<button style={Object.entries(style).map(([k,v]) => `${k}:${v}`).join(';')}>
|
|
1144
|
-
Click me
|
|
1145
|
-
</button>
|
|
1146
|
-
```
|
|
1147
|
-
|
|
1148
|
-
### Vanilla JS
|
|
1149
|
-
|
|
1150
|
-
```javascript
|
|
1151
|
-
import { tws, twsx } from 'tailwind-to-style'
|
|
1152
|
-
|
|
1153
|
-
// Inline styles
|
|
1154
|
-
const el = document.createElement('button')
|
|
1155
|
-
Object.assign(el.style, tws('bg-blue-500 text-white px-4 py-2 rounded-lg', true))
|
|
1156
|
-
|
|
1157
|
-
// Inject global styles
|
|
1158
|
-
twsx({
|
|
1159
|
-
'.card': 'bg-white p-6 rounded-lg shadow-md',
|
|
1160
|
-
'.card:hover': 'shadow-xl',
|
|
1161
|
-
})
|
|
1162
|
-
```
|
|
1163
|
-
|
|
1164
|
-
---
|
|
1165
|
-
|
|
1166
|
-
## Performance
|
|
1167
|
-
|
|
1168
|
-
v3.2.0 includes major performance optimizations:
|
|
1169
|
-
|
|
1170
|
-
- **Pre-compiled regex** โ compiled once at module load, reused for every call
|
|
1171
|
-
- **Multi-level LRU caching** โ class resolution, CSS generation, config lookups
|
|
1172
|
-
- **Bounded caches** โ Maps capped at 5,000 entries, Sets at 10,000 to prevent memory leaks
|
|
1173
|
-
- **Slot-based CSS injection** โ each `twsx()` call owns a named slot; updates rebuild the tag instead of appending, preventing HMR accumulation
|
|
1174
|
-
- **FNV-1a hashing** โ 100x faster than `JSON.stringify` for cache keys
|
|
1175
|
-
|
|
1176
|
-
```
|
|
1177
|
-
Parse 10,000 classes:
|
|
1178
|
-
Cold: ~12ms
|
|
1179
|
-
Cached: ~0.12ms (100x faster)
|
|
1180
|
-
|
|
1181
|
-
Bundle sizes:
|
|
1182
|
-
Full import: ~12KB minified
|
|
1183
|
-
tws() only: ~3KB minified
|
|
1184
|
-
twsx() only: ~6KB minified
|
|
1185
|
-
```
|
|
1186
|
-
|
|
1187
|
-
**Performance utilities:**
|
|
1188
|
-
|
|
1189
|
-
```javascript
|
|
1190
|
-
import { performanceUtils } from 'tailwind-to-style'
|
|
1191
|
-
|
|
1192
|
-
// View cache stats
|
|
1193
|
-
performanceUtils.getStats()
|
|
1194
|
-
|
|
1195
|
-
// Clear all caches
|
|
1196
|
-
performanceUtils.clearCaches()
|
|
1197
|
-
|
|
1198
|
-
// Enable performance logging
|
|
1199
|
-
performanceUtils.enablePerformanceLogging(true)
|
|
1200
|
-
```
|
|
1201
|
-
|
|
1202
|
-
---
|
|
1203
|
-
|
|
1204
|
-
## Debugging & Logging
|
|
1205
|
-
|
|
1206
|
-
Logging is disabled by default. Enable via environment variable or programmatically:
|
|
1207
|
-
|
|
1208
|
-
```bash
|
|
1209
|
-
TWSX_LOG_LEVEL=debug npm start # debug, info, warn, error, silent
|
|
1210
|
-
```
|
|
1211
|
-
|
|
1212
|
-
```javascript
|
|
1213
|
-
import { logger } from 'tailwind-to-style'
|
|
1214
|
-
|
|
1215
|
-
logger.setLevel('debug')
|
|
1216
|
-
console.log(logger.getLevel()) // โ 'debug'
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
| Level | Description |
|
|
1220
|
-
|---|---|
|
|
1221
|
-
| `debug` | Detailed processing info |
|
|
1222
|
-
| `info` | General information |
|
|
1223
|
-
| `warn` | Performance warnings |
|
|
1224
|
-
| `error` | Errors only |
|
|
1225
|
-
| `silent` | No logging (default) |
|
|
1226
|
-
|
|
1227
|
-
---
|
|
1228
|
-
|
|
1229
|
-
## Comparison
|
|
1230
|
-
|
|
1231
|
-
| Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS | tailwind-variants |
|
|
1232
|
-
|---|:---:|:---:|:---:|:---:|
|
|
1233
|
-
| Build Step | None | Required | None | Required |
|
|
1234
|
-
| Bundle Size | 3-12KB | ~80KB+ | 20-40KB | ~15KB |
|
|
1235
|
-
| Runtime Styles | Yes | No | Yes | Partial |
|
|
1236
|
-
| Full Tailwind Support | Yes | Yes | No | Classes only |
|
|
1237
|
-
| SSR Support | Yes | Yes | Depends | Yes |
|
|
1238
|
-
| Variant System | Built-in | No | No | Yes |
|
|
1239
|
-
| Conditional Classes | `cx()` | No | No | `tv()` |
|
|
1240
|
-
| SCSS-like Nesting | Yes | Plugins | Yes | No |
|
|
1241
|
-
| @css Raw Injection | Yes | No | Yes | No |
|
|
1242
|
-
| Framework Agnostic | Yes | Yes | Depends | Yes |
|
|
1243
|
-
| Tree-Shaking | Yes | Partial | Yes | Yes |
|
|
1244
|
-
| TypeScript Generics | Yes | Yes | Yes | Yes |
|
|
1245
|
-
| Zero Dependencies | Yes | PostCSS | No | tailwind-merge |
|
|
1246
|
-
|
|
1247
|
-
---
|
|
1248
|
-
|
|
1249
|
-
## Migration from v2
|
|
1250
|
-
|
|
1251
|
-
See [MIGRATION.md](MIGRATION.md) for the detailed guide from v2.x to v3.x.
|
|
1252
|
-
|
|
1253
|
-
**Quick summary:**
|
|
1254
|
-
|
|
1255
|
-
| Status | API |
|
|
1256
|
-
|---|---|
|
|
1257
|
-
| No changes | `tws()`, `twsx()`, `configure()` |
|
|
1258
|
-
| New in v3.1 | `twsxVariants()` |
|
|
1259
|
-
| New in v3.2 | `cx()`, SSR, tree-shakeable imports |
|
|
1260
|
-
| Removed | `styled()`, `tv()`, `useTwsx()`, `TwsxProvider`, CLI tools |
|
|
1261
|
-
|
|
1262
|
-
---
|
|
1263
|
-
|
|
1264
|
-
## Contributing
|
|
1265
|
-
|
|
1266
|
-
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for architecture overview, testing guidelines, and build output docs.
|
|
1267
|
-
|
|
1268
|
-
---
|
|
1269
|
-
|
|
1270
|
-
## ๐ Support
|
|
1271
|
-
|
|
1272
|
-
If you find this library helpful, consider supporting:
|
|
1273
|
-
|
|
1274
|
-
[โ Buy me a coffee](https://buymeacoffee.com/bigetion)
|
|
388
|
+
- **React** โ Full bindings via `tailwind-to-style/react` (example available in `examples/react-demo`)
|
|
389
|
+
- **Vanilla JS** โ Direct DOM usage with `tw()` and `tws()`
|
|
390
|
+
- **Node.js / SSR** โ `tws()` for inline styles + `createSSRCollector()` for CSS extraction
|
|
391
|
+
- **Vue / Svelte** โ supported in runtime with `tw()` / `tws()`, examples can be added in future releases
|
|
1275
392
|
|
|
1276
393
|
---
|
|
1277
394
|
|
|
1278
395
|
## License
|
|
1279
396
|
|
|
1280
397
|
MIT ยฉ [Bigetion](https://github.com/Bigetion)
|
|
1281
|
-
|
|
1282
|
-
---
|
|
1283
|
-
|
|
1284
|
-
**v3.2.0** โ [Changelog](CHANGELOG.md) ยท [Architecture](ARCHITECTURE.md) ยท [Migration Guide](MIGRATION.md)
|