tailwind-to-style 2.12.6 â 3.1.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 +350 -2275
- package/README.v2-backup.md +2456 -0
- package/dist/index.cjs +731 -5384
- package/dist/index.d.ts +101 -0
- package/dist/index.esm.js +731 -5375
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +3 -38
- package/dist/index.browser.js +0 -10009
- package/dist/react.cjs.js +0 -10085
- package/dist/react.d.ts +0 -76
- package/dist/react.esm.js +0 -10069
- package/lib/twsx-cli.js +0 -182
- package/plugins/vite-twsx.js +0 -161
- package/plugins/webpack-twsx.js +0 -137
package/README.md
CHANGED
|
@@ -7,327 +7,243 @@
|
|
|
7
7
|
[](https://www.npmjs.com/package/tailwind-to-style)
|
|
8
8
|
[](https://github.com/Bigetion/tailwind-to-style/blob/main/LICENSE)
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
> **Runtime Tailwind CSS to inline styles converter**
|
|
11
|
+
>
|
|
12
|
+
> Simple, fast, framework-agnostic. Convert Tailwind utility classes to inline styles or CSS at runtime with zero build step.
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
## ⥠Why tailwind-to-style?
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
- **ð Zero Build Step** - No PostCSS, no compilation, just JavaScript
|
|
17
|
+
- **ðĶ Framework Agnostic** - Works with React, Vue, Svelte, vanilla JS
|
|
18
|
+
- **ðĻ Full Tailwind Support** - All utilities, responsive, pseudo-states, arbitrary values
|
|
19
|
+
- **ðĨ SCSS-like Nesting** - Write complex nested styles with ease
|
|
20
|
+
- **âïļ Customizable** - Extend theme with your colors, spacing, fonts
|
|
21
|
+
- **ðŠ TypeScript Support** - Full type definitions included
|
|
22
|
+
- **ðŠķ Lightweight** - ~12KB minified (70% smaller than v2)
|
|
17
23
|
|
|
18
|
-
##
|
|
24
|
+
## ðĨ Installation
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **Custom Separator**: Use `-`, `_`, `__` or any separator
|
|
23
|
-
- **Flexible Hash Length**: Adjust uniqueness vs size (4-8 characters)
|
|
24
|
-
- **Component Name Toggle**: Include/exclude component type in classname
|
|
25
|
-
- **Per-Component Override**: Different naming for specific components
|
|
26
|
-
- **Design System Ready**: Perfect for branded design systems
|
|
27
|
-
|
|
28
|
-
**Example:**
|
|
29
|
-
```javascript
|
|
30
|
-
// Global config
|
|
31
|
-
configure({ styled: { prefix: 'myapp', separator: '_' }})
|
|
32
|
-
|
|
33
|
-
const Button = styled('button', { base: 'px-4 py-2' })
|
|
34
|
-
// Generates: myapp_button_a3k9x (instead of twsx-button-a3k9x2)
|
|
26
|
+
```bash
|
|
27
|
+
npm install tailwind-to-style
|
|
35
28
|
```
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
### Previous Updates (v2.12.0)
|
|
40
|
-
|
|
41
|
-
- **ð Complete Optimization Suite**: Production-ready performance tools
|
|
42
|
-
- **Bundle Analysis**: Analyze size, get recommendations, export reports
|
|
43
|
-
- **Build-time Extraction**: Static CSS generation for faster loads
|
|
44
|
-
- **Critical CSS**: Above-the-fold CSS extraction for instant renders
|
|
45
|
-
- **CSS Purging**: Automatic removal of unused styles (tree-shaking)
|
|
46
|
-
- **Advanced Caching**: Persistent cache with compression (localStorage/IndexedDB)
|
|
47
|
-
- **Optimization Manager**: Unified API with presets (minimal/balanced/aggressive)
|
|
48
|
-
|
|
49
|
-
- **ð Performance Benefits**:
|
|
50
|
-
- Up to 82% bundle size reduction
|
|
51
|
-
- 75% faster initial load times
|
|
52
|
-
- 76% faster time to interactive
|
|
53
|
-
- Automatic CSS deduplication
|
|
54
|
-
- Memory-efficient LRU caching
|
|
55
|
-
|
|
56
|
-
**[ð See Full Optimization Guide â](OPTIMIZATION_GUIDE.md)**
|
|
57
|
-
|
|
58
|
-
### Previous Updates (v2.11.0)
|
|
59
|
-
|
|
60
|
-
- **ðĻ Styled Components System**: Create reusable components with `styled()` factory
|
|
61
|
-
- Variant-based styling inspired by styled-components and twin.macro
|
|
62
|
-
- Tag helpers: `styled.div()`, `styled.button()`, etc.
|
|
63
|
-
- Pseudo-state support: hover, focus, active, disabled
|
|
64
|
-
- Nested styles with SCSS-like syntax
|
|
65
|
-
- Polymorphic "as" prop for component flexibility
|
|
66
|
-
- Full TypeScript support with type inference
|
|
67
|
-
|
|
68
|
-
- **ð Type-safe Variants**: Framework-agnostic `tv()` for design systems
|
|
69
|
-
- Compound variants for complex conditions
|
|
70
|
-
- Default variants support
|
|
71
|
-
- Full TypeScript integration
|
|
72
|
-
- Works with any framework or vanilla JS
|
|
73
|
-
- `createVariants()` for batch variant creation
|
|
74
|
-
|
|
75
|
-
### Previous Updates (v2.10.5)
|
|
76
|
-
|
|
77
|
-
- **ðŽ Complete Animation System**: Full support for Tailwind animations and transitions
|
|
78
|
-
- Built-in animations: `animate-spin`, `animate-ping`, `animate-pulse`, `animate-bounce`
|
|
79
|
-
- Complete transition utilities with duration, delay, and easing controls
|
|
80
|
-
- Custom animations via theme configuration
|
|
81
|
-
- Keyframes system with built-in and custom support
|
|
82
|
-
|
|
83
|
-
- **ðĻ Theme Customization**: Extend default theme with custom colors, spacing, and more!
|
|
84
|
-
- Deep merge support for nested theme values
|
|
85
|
-
- Works seamlessly with existing Tailwind utilities
|
|
86
|
-
- Brand-specific design systems
|
|
87
|
-
|
|
88
|
-
- **ð Plugin API**: Create custom utilities with `createPlugin()` and `createUtilityPlugin()`
|
|
89
|
-
- Simple utility plugins for custom styles
|
|
90
|
-
- Dynamic utilities with multiple values
|
|
91
|
-
- Unlimited custom utility classes
|
|
92
|
-
|
|
93
|
-
- **âïļ Configuration System**: Use `configure()` to set up theme and plugins
|
|
94
|
-
- Support for `tailwind-to-style.config.js`
|
|
95
|
-
- Prefix support and core plugin control
|
|
96
|
-
- Easy configuration management
|
|
97
|
-
|
|
98
|
-
- **ð Infrastructure Improvements**:
|
|
99
|
-
- Updated dependencies (ESLint 9, Jest 30, Rollup 4)
|
|
100
|
-
- LRU Cache for better memory management
|
|
101
|
-
- Configurable logger system (production-safe)
|
|
102
|
-
- Event-based error handling
|
|
103
|
-
- Complete TypeScript definitions
|
|
104
|
-
- Node.js 18.x, 20.x, 22.x LTS support
|
|
105
|
-
|
|
106
|
-
- **ðą Responsive Selector Syntax**: Intuitive `'md:.title': 'text-lg'` format
|
|
107
|
-
- **ð Enhanced @css Directive**: Perfect CSS variables and functions preservation
|
|
30
|
+
## ðŊ Quick Start
|
|
108
31
|
|
|
109
|
-
|
|
32
|
+
### Simple Conversion with `tws()`
|
|
110
33
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
### Styled Components
|
|
34
|
+
Convert Tailwind classes to style objects:
|
|
114
35
|
|
|
115
36
|
```javascript
|
|
116
|
-
import {
|
|
37
|
+
import { tws } from 'tailwind-to-style'
|
|
117
38
|
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
123
|
-
danger: 'bg-red-500 text-white hover:bg-red-600'
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
})
|
|
39
|
+
const styles = tws('bg-blue-500 text-white p-4 rounded-lg hover:bg-blue-600')
|
|
40
|
+
|
|
41
|
+
// Use in React
|
|
42
|
+
<div style={styles}>Hello World</div>
|
|
127
43
|
|
|
128
|
-
|
|
44
|
+
// Use in vanilla JS
|
|
45
|
+
element.style = Object.assign(element.style, styles)
|
|
129
46
|
```
|
|
130
47
|
|
|
131
|
-
###
|
|
48
|
+
### Nested Styles with `twsx()`
|
|
49
|
+
|
|
50
|
+
Create complex styles with SCSS-like nesting:
|
|
132
51
|
|
|
133
52
|
```javascript
|
|
134
|
-
import {
|
|
53
|
+
import { twsx } from 'tailwind-to-style'
|
|
135
54
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
55
|
+
const css = twsx({
|
|
56
|
+
'.card': [
|
|
57
|
+
'bg-white p-6 rounded-lg shadow-md',
|
|
58
|
+
{
|
|
59
|
+
'&:hover': 'shadow-xl transform scale-105',
|
|
60
|
+
'> .title': 'text-2xl font-bold text-gray-900 mb-2',
|
|
61
|
+
'> .description': 'text-gray-600 leading-relaxed',
|
|
62
|
+
'> .button': [
|
|
63
|
+
'bg-blue-500 text-white px-4 py-2 rounded',
|
|
64
|
+
{
|
|
65
|
+
'&:hover': 'bg-blue-600',
|
|
66
|
+
'&:active': 'bg-blue-700'
|
|
67
|
+
}
|
|
68
|
+
]
|
|
142
69
|
}
|
|
70
|
+
],
|
|
71
|
+
|
|
72
|
+
// Media queries at root level
|
|
73
|
+
'@media (max-width: 768px)': {
|
|
74
|
+
'.card': 'p-4',
|
|
75
|
+
'.card > .title': 'text-xl'
|
|
143
76
|
}
|
|
144
77
|
})
|
|
145
78
|
|
|
146
|
-
|
|
79
|
+
// Inject to document
|
|
80
|
+
const style = document.createElement('style')
|
|
81
|
+
style.textContent = css
|
|
82
|
+
document.head.appendChild(style)
|
|
147
83
|
```
|
|
148
84
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
## Installation
|
|
85
|
+
## ð Core API
|
|
152
86
|
|
|
153
|
-
|
|
154
|
-
npm install tailwind-to-style
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## Quick Start: Optimization (v2.12.0+)
|
|
87
|
+
### `tws(classes, options?)`
|
|
158
88
|
|
|
159
|
-
|
|
89
|
+
Convert Tailwind classes to inline style object.
|
|
160
90
|
|
|
161
91
|
```javascript
|
|
162
|
-
import {
|
|
163
|
-
|
|
164
|
-
// Quick API - Analyze bundle size
|
|
165
|
-
const analysis = await optimize.analyzeBundle();
|
|
166
|
-
console.log(`Bundle: ${analysis.totalSize} bytes, Gzip: ${analysis.gzipSize} bytes`);
|
|
167
|
-
|
|
168
|
-
// Quick API - Purge unused CSS (82% reduction!)
|
|
169
|
-
const { css, stats } = await optimize.purgeCSS({
|
|
170
|
-
content: ['src/**/*.{js,jsx,ts,tsx}'],
|
|
171
|
-
css: yourCSSString
|
|
172
|
-
});
|
|
173
|
-
console.log(`Saved ${stats.rulesRemoved} rules, ${((1 - stats.purgedSize / stats.originalSize) * 100).toFixed(1)}% reduction`);
|
|
174
|
-
|
|
175
|
-
// Quick API - Extract critical CSS
|
|
176
|
-
const critical = await optimize.extractCritical({
|
|
177
|
-
html: yourHTMLString,
|
|
178
|
-
minify: true
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// OR use Optimization Manager with presets
|
|
182
|
-
const optimizer = createOptimizationManager('aggressive'); // or 'balanced', 'minimal'
|
|
183
|
-
await optimizer.initialize();
|
|
184
|
-
|
|
185
|
-
const results = await optimizer.optimize(css, ['src/**/*.jsx']);
|
|
186
|
-
console.log(`Optimized: ${results.stats.overall.savings}% size reduction`);
|
|
187
|
-
optimizer.generateReport(); // Detailed analysis
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
**Performance Benefits:**
|
|
191
|
-
- ð Up to 82% bundle size reduction
|
|
192
|
-
- ⥠75% faster initial load times
|
|
193
|
-
- ðĶ Automatic CSS deduplication
|
|
194
|
-
- ðū Persistent caching with compression
|
|
92
|
+
import { tws } from 'tailwind-to-style'
|
|
195
93
|
|
|
196
|
-
|
|
94
|
+
// Basic usage
|
|
95
|
+
const styles = tws('flex items-center gap-4')
|
|
96
|
+
// â { display: 'flex', alignItems: 'center', gap: '1rem' }
|
|
197
97
|
|
|
198
|
-
|
|
98
|
+
// Responsive classes
|
|
99
|
+
const styles = tws('text-sm md:text-base lg:text-lg')
|
|
199
100
|
|
|
200
|
-
|
|
101
|
+
// Pseudo-states
|
|
102
|
+
const styles = tws('bg-blue-500 hover:bg-blue-600 focus:ring-2')
|
|
201
103
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
import 'tailwind-to-style/preflight.css'
|
|
205
|
-
import { TwsxProvider } from 'tailwind-to-style'
|
|
104
|
+
// Arbitrary values
|
|
105
|
+
const styles = tws('w-[123px] text-[#abc] m-[1.5rem]')
|
|
206
106
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
```
|
|
107
|
+
// Important modifier
|
|
108
|
+
const styles = tws('!bg-red-500')
|
|
211
109
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
110
|
+
// Return as JSON string
|
|
111
|
+
const json = tws('p-4 m-2', { format: 'json' })
|
|
112
|
+
// â '{"padding":"1rem","margin":"0.5rem"}'
|
|
215
113
|
```
|
|
216
114
|
|
|
217
|
-
|
|
218
|
-
- Consistent box-sizing
|
|
219
|
-
- Reset margins and paddings
|
|
220
|
-
- Normalized form elements
|
|
221
|
-
- Better default font rendering
|
|
222
|
-
|
|
223
|
-
**Note:** If you're already using Tailwind CSS in your project, you don't need to import this.
|
|
115
|
+
### `twsx(styleObject, options?)`
|
|
224
116
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
### Quick Start with React
|
|
117
|
+
Generate CSS from nested style definitions with Tailwind classes.
|
|
228
118
|
|
|
229
119
|
```javascript
|
|
230
|
-
import {
|
|
120
|
+
import { twsx } from 'tailwind-to-style'
|
|
231
121
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
122
|
+
const css = twsx({
|
|
123
|
+
'.button': [
|
|
124
|
+
'bg-blue-500 text-white px-6 py-3 rounded-lg font-medium transition-all',
|
|
125
|
+
{
|
|
126
|
+
'&:hover': 'bg-blue-600 transform scale-105',
|
|
127
|
+
'&:active': 'bg-blue-700 scale-95',
|
|
128
|
+
'&:disabled': 'bg-gray-400 opacity-50 cursor-not-allowed',
|
|
129
|
+
'&.large': 'px-8 py-4 text-lg',
|
|
130
|
+
'&.small': 'px-3 py-1.5 text-sm'
|
|
239
131
|
}
|
|
132
|
+
],
|
|
133
|
+
|
|
134
|
+
'.card': 'bg-white rounded-xl shadow-lg overflow-hidden',
|
|
135
|
+
'.card > .header': 'p-6 border-b border-gray-200',
|
|
136
|
+
'.card > .body': 'p-6',
|
|
137
|
+
'.card > .footer': 'p-6 bg-gray-50',
|
|
138
|
+
|
|
139
|
+
// Media queries at root level
|
|
140
|
+
'@media (max-width: 768px)': {
|
|
141
|
+
'.card': 'rounded-lg',
|
|
142
|
+
'.card > .header': 'p-4',
|
|
143
|
+
'.card > .body': 'p-4'
|
|
240
144
|
}
|
|
241
|
-
}
|
|
145
|
+
})
|
|
242
146
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
</TwsxProvider>
|
|
248
|
-
)
|
|
249
|
-
}
|
|
147
|
+
// Options
|
|
148
|
+
const minified = twsx(styles, { minify: true })
|
|
149
|
+
const formatted = twsx(styles, { format: 'pretty' })
|
|
150
|
+
```
|
|
250
151
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
'&:hover': 'bg-brand-600 transform scale-105',
|
|
258
|
-
'.title': 'text-xl font-bold mb-2'
|
|
259
|
-
}
|
|
260
|
-
]
|
|
261
|
-
})
|
|
152
|
+
**Nesting Syntax:**
|
|
153
|
+
- `'&:hover'` - Pseudo-classes
|
|
154
|
+
- `'&.class'` - Modifiers
|
|
155
|
+
- `'> .child'` - Direct children
|
|
156
|
+
- `'.nested'` - Descendants
|
|
157
|
+
- `'@media ...'` - Media queries (root level only)
|
|
262
158
|
|
|
263
|
-
|
|
264
|
-
<div className="card">
|
|
265
|
-
<h2 className="title">Interactive Card</h2>
|
|
266
|
-
<p>Hover me to see the effect!</p>
|
|
267
|
-
</div>
|
|
268
|
-
)
|
|
269
|
-
}
|
|
270
|
-
```
|
|
159
|
+
### `twsxVariants(className, config)`
|
|
271
160
|
|
|
272
|
-
|
|
161
|
+
Create variant-based component styles with automatic CSS generation. Similar to `tailwind-variants` but with auto-injection.
|
|
273
162
|
|
|
274
163
|
```javascript
|
|
275
|
-
|
|
276
|
-
import { useTwsx, TwsxProvider } from 'tailwind-to-style'
|
|
164
|
+
import { twsxVariants } from 'tailwind-to-style'
|
|
277
165
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
166
|
+
const btn = twsxVariants('.btn', {
|
|
167
|
+
base: 'px-4 py-2 rounded-lg font-medium transition-all',
|
|
168
|
+
variants: {
|
|
169
|
+
variant: {
|
|
170
|
+
solid: 'border-transparent',
|
|
171
|
+
outline: 'bg-transparent border-2',
|
|
172
|
+
ghost: 'bg-transparent',
|
|
173
|
+
},
|
|
174
|
+
color: {
|
|
175
|
+
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
176
|
+
danger: 'bg-red-500 text-white hover:bg-red-600',
|
|
177
|
+
},
|
|
178
|
+
size: {
|
|
179
|
+
sm: 'px-3 py-1.5 text-sm',
|
|
180
|
+
md: 'px-4 py-2 text-base',
|
|
181
|
+
lg: 'px-6 py-3 text-lg',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
compoundVariants: [
|
|
185
|
+
{ variant: 'outline', color: 'primary', class: 'border-blue-500 text-blue-600' },
|
|
186
|
+
{ variant: 'outline', color: 'danger', class: 'border-red-500 text-red-600' },
|
|
187
|
+
],
|
|
188
|
+
defaultVariants: { variant: 'solid', color: 'primary', size: 'md' }
|
|
189
|
+
})
|
|
281
190
|
|
|
282
|
-
|
|
191
|
+
// Usage - returns class name string
|
|
192
|
+
btn() // "btn"
|
|
193
|
+
btn({ color: 'danger' }) // "btn btn-danger"
|
|
194
|
+
btn({ variant: 'outline', size: 'lg' }) // "btn btn-outline-lg"
|
|
195
|
+
|
|
196
|
+
// In React
|
|
197
|
+
const Button = ({ variant, color, size, children, ...props }) => (
|
|
198
|
+
<button className={btn({ variant, color, size })} {...props}>
|
|
199
|
+
{children}
|
|
200
|
+
</button>
|
|
201
|
+
)
|
|
202
|
+
```
|
|
283
203
|
|
|
284
|
-
|
|
204
|
+
**Nested Selectors** - Style child elements:
|
|
285
205
|
|
|
286
206
|
```javascript
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Get CSS without injection
|
|
303
|
-
const css = useTwsx({
|
|
304
|
-
'.card': 'bg-white p-6 rounded-lg shadow-md'
|
|
305
|
-
}, { inject: false })
|
|
207
|
+
const alert = twsxVariants('.alert', {
|
|
208
|
+
base: 'p-4 rounded-lg border flex gap-3',
|
|
209
|
+
variants: {
|
|
210
|
+
status: {
|
|
211
|
+
info: 'bg-blue-50 text-blue-800',
|
|
212
|
+
error: 'bg-red-50 text-red-800',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
defaultVariants: { status: 'info' },
|
|
216
|
+
nested: {
|
|
217
|
+
'.alert-icon': 'flex-shrink-0 mt-0.5',
|
|
218
|
+
'.alert-content': 'flex-1',
|
|
219
|
+
'.alert-dismiss': 'p-1 rounded hover:bg-black/10',
|
|
220
|
+
}
|
|
221
|
+
})
|
|
306
222
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
<div className="card">
|
|
311
|
-
<button className="button">Click me</button>
|
|
312
|
-
</div>
|
|
313
|
-
</>
|
|
314
|
-
)
|
|
315
|
-
}
|
|
223
|
+
// Generates CSS:
|
|
224
|
+
// .alert .alert-icon { ... }
|
|
225
|
+
// .alert .alert-content { ... }
|
|
316
226
|
```
|
|
317
227
|
|
|
318
|
-
|
|
228
|
+
**Class Naming Convention:**
|
|
229
|
+
- `.btn` = all defaults
|
|
230
|
+
- `.btn-outline` = outline variant (non-default)
|
|
231
|
+
- `.btn-outline-danger-lg` = multiple non-defaults
|
|
232
|
+
|
|
233
|
+
### `configure(config)`
|
|
319
234
|
|
|
320
|
-
|
|
235
|
+
Customize theme with your colors, spacing, fonts, and more.
|
|
321
236
|
|
|
322
237
|
```javascript
|
|
323
|
-
import {
|
|
238
|
+
import { configure } from 'tailwind-to-style'
|
|
324
239
|
|
|
325
|
-
|
|
240
|
+
configure({
|
|
326
241
|
theme: {
|
|
327
242
|
extend: {
|
|
328
243
|
colors: {
|
|
329
244
|
brand: {
|
|
330
245
|
50: '#eff6ff',
|
|
246
|
+
100: '#dbeafe',
|
|
331
247
|
500: '#3b82f6',
|
|
332
248
|
600: '#2563eb',
|
|
333
249
|
900: '#1e3a8a'
|
|
@@ -337,2120 +253,279 @@ const themeConfig = {
|
|
|
337
253
|
spacing: {
|
|
338
254
|
'128': '32rem',
|
|
339
255
|
'144': '36rem'
|
|
256
|
+
},
|
|
257
|
+
fontFamily: {
|
|
258
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
259
|
+
mono: ['Fira Code', 'monospace']
|
|
340
260
|
}
|
|
341
261
|
}
|
|
342
262
|
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
function App() {
|
|
346
|
-
return (
|
|
347
|
-
<TwsxProvider config={themeConfig}>
|
|
348
|
-
<Header />
|
|
349
|
-
<Main />
|
|
350
|
-
<Footer />
|
|
351
|
-
</TwsxProvider>
|
|
352
|
-
)
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
function Header() {
|
|
356
|
-
useTwsx({
|
|
357
|
-
'.header': [
|
|
358
|
-
'bg-brand-500 text-white p-128', // Uses custom spacing
|
|
359
|
-
{
|
|
360
|
-
'.logo': 'text-accent font-bold text-2xl', // Uses custom color
|
|
361
|
-
'&:hover': 'bg-brand-600'
|
|
362
|
-
}
|
|
363
|
-
]
|
|
364
|
-
})
|
|
263
|
+
})
|
|
365
264
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
<div className="logo">My Brand</div>
|
|
369
|
-
</header>
|
|
370
|
-
)
|
|
371
|
-
}
|
|
265
|
+
// Now use your custom theme
|
|
266
|
+
const styles = tws('bg-brand-500 text-brand-50 p-128 font-sans')
|
|
372
267
|
```
|
|
373
268
|
|
|
374
|
-
|
|
269
|
+
**Configuration File:**
|
|
375
270
|
|
|
376
|
-
Create
|
|
271
|
+
Create `tailwind-to-style.config.js` in your project root:
|
|
377
272
|
|
|
378
273
|
```javascript
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
useTwsx({
|
|
386
|
-
'.theme-container': [
|
|
387
|
-
`bg-${theme === 'dark' ? 'gray-900' : 'white'} p-6 rounded-lg transition-all duration-300`,
|
|
388
|
-
{
|
|
389
|
-
[`&.${theme}`]: theme === 'dark'
|
|
390
|
-
? 'text-white border-gray-700'
|
|
391
|
-
: 'text-gray-900 border-gray-200',
|
|
392
|
-
'.theme-title': 'text-2xl font-bold mb-4',
|
|
393
|
-
'.theme-button': [
|
|
394
|
-
'px-4 py-2 rounded-lg font-medium transition-colors',
|
|
395
|
-
theme === 'dark'
|
|
396
|
-
? 'bg-yellow-500 text-gray-900 hover:bg-yellow-400'
|
|
397
|
-
: 'bg-gray-800 text-white hover:bg-gray-700'
|
|
398
|
-
]
|
|
274
|
+
export default {
|
|
275
|
+
theme: {
|
|
276
|
+
extend: {
|
|
277
|
+
colors: {
|
|
278
|
+
primary: '#3b82f6',
|
|
279
|
+
secondary: '#10b981'
|
|
399
280
|
}
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return (
|
|
404
|
-
<div className={`theme-container ${theme}`}>
|
|
405
|
-
<h2 className="theme-title">ð Dynamic Theme</h2>
|
|
406
|
-
<button
|
|
407
|
-
className="theme-button"
|
|
408
|
-
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
|
409
|
-
>
|
|
410
|
-
Switch to {theme === 'light' ? 'Dark' : 'Light'} Mode
|
|
411
|
-
</button>
|
|
412
|
-
</div>
|
|
413
|
-
)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
414
283
|
}
|
|
415
284
|
```
|
|
416
285
|
|
|
417
|
-
|
|
286
|
+
## ðĻ Preflight CSS (Base Styles)
|
|
287
|
+
|
|
288
|
+
For best results, import Tailwind's preflight (base styles):
|
|
418
289
|
|
|
419
290
|
```javascript
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
// Example usage
|
|
429
|
-
function ConfigAwareComponent() {
|
|
430
|
-
const { config, isConfigured } = useTwsxConfig()
|
|
431
|
-
const updateConfig = useUpdateTwsxConfig()
|
|
432
|
-
|
|
433
|
-
if (!isConfigured) {
|
|
434
|
-
return <div>Loading theme...</div>
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return (
|
|
438
|
-
<div>
|
|
439
|
-
<p>Current theme: {config.theme?.extend?.colors?.brand ? 'Custom' : 'Default'}</p>
|
|
440
|
-
<button onClick={() => updateConfig({
|
|
441
|
-
theme: { extend: { colors: { brand: { 500: '#ef4444' } } } }
|
|
442
|
-
})}>
|
|
443
|
-
Change Brand Color
|
|
444
|
-
</button>
|
|
445
|
-
</div>
|
|
446
|
-
)
|
|
447
|
-
}
|
|
291
|
+
// In your main entry file
|
|
292
|
+
import 'tailwind-to-style/preflight.css'
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
```html
|
|
296
|
+
<!-- Or in HTML -->
|
|
297
|
+
<link rel="stylesheet" href="node_modules/tailwind-to-style/preflight.css">
|
|
448
298
|
```
|
|
449
299
|
|
|
450
|
-
|
|
300
|
+
The preflight CSS provides:
|
|
301
|
+
- Consistent box-sizing
|
|
302
|
+
- Reset margins and paddings
|
|
303
|
+
- Normalized form elements
|
|
304
|
+
- Better default font rendering
|
|
451
305
|
|
|
452
|
-
**
|
|
306
|
+
**Note:** Skip this if you're already using Tailwind CSS in your project.
|
|
453
307
|
|
|
454
|
-
|
|
308
|
+
## ðĄ Use Cases
|
|
455
309
|
|
|
456
|
-
|
|
457
|
-
- ð **Optimized CSS Injection**: Global cache prevents duplicate styles, single `<style>` tag for better performance
|
|
458
|
-
- ðŊ **Type-safe Variants**: Full TypeScript support with automatic type inference
|
|
459
|
-
- ð **SSR-Compatible**: Same class names on server and client, no hydration mismatches
|
|
460
|
-
- ðĶ **Zero Runtime Overhead**: Styles are generated once and cached efficiently
|
|
310
|
+
### 1. Dynamic Styling
|
|
461
311
|
|
|
462
|
-
|
|
312
|
+
Generate styles from user input or runtime data:
|
|
463
313
|
|
|
464
314
|
```javascript
|
|
465
|
-
import {
|
|
466
|
-
|
|
467
|
-
const Button = styled('button', {
|
|
468
|
-
base: 'px-4 py-2 rounded-lg font-medium transition-all',
|
|
469
|
-
variants: {
|
|
470
|
-
color: {
|
|
471
|
-
primary: 'bg-blue-500 text-white hover:bg-blue-600',
|
|
472
|
-
secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
|
|
473
|
-
danger: 'bg-red-500 text-white hover:bg-red-600'
|
|
474
|
-
},
|
|
475
|
-
size: {
|
|
476
|
-
sm: 'text-sm px-3 py-1.5',
|
|
477
|
-
md: 'text-base px-4 py-2',
|
|
478
|
-
lg: 'text-lg px-6 py-3'
|
|
479
|
-
},
|
|
480
|
-
outlined: {
|
|
481
|
-
true: 'bg-transparent border-2'
|
|
482
|
-
}
|
|
483
|
-
},
|
|
484
|
-
defaultVariants: {
|
|
485
|
-
color: 'primary',
|
|
486
|
-
size: 'md'
|
|
487
|
-
}
|
|
488
|
-
})
|
|
315
|
+
import { tws } from 'tailwind-to-style'
|
|
489
316
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
return
|
|
493
|
-
<>
|
|
494
|
-
<Button>Default Button</Button>
|
|
495
|
-
<Button color="secondary" size="lg">Large Secondary</Button>
|
|
496
|
-
<Button color="danger" outlined>Outlined Danger</Button>
|
|
497
|
-
</>
|
|
498
|
-
)
|
|
317
|
+
function UserCard({ user }) {
|
|
318
|
+
const styles = tws(`bg-${user.color}-500 p-4 rounded-lg`)
|
|
319
|
+
return <div style={styles}>{user.name}</div>
|
|
499
320
|
}
|
|
500
321
|
```
|
|
501
322
|
|
|
502
|
-
###
|
|
323
|
+
### 2. Email Templates
|
|
503
324
|
|
|
504
|
-
|
|
325
|
+
Generate inline styles for email HTML:
|
|
505
326
|
|
|
506
327
|
```javascript
|
|
507
|
-
import {
|
|
508
|
-
|
|
509
|
-
const Container = styled.div({
|
|
510
|
-
base: 'max-w-7xl mx-auto px-4 sm:px-6 lg:px-8'
|
|
511
|
-
})
|
|
512
|
-
|
|
513
|
-
const Title = styled.h1({
|
|
514
|
-
base: 'text-4xl font-bold text-gray-900',
|
|
515
|
-
variants: {
|
|
516
|
-
centered: {
|
|
517
|
-
true: 'text-center'
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
})
|
|
521
|
-
|
|
522
|
-
const Card = styled.article({
|
|
523
|
-
base: 'bg-white rounded-lg shadow-md p-6',
|
|
524
|
-
hover: 'shadow-xl transform scale-105',
|
|
525
|
-
active: 'shadow-lg scale-100'
|
|
526
|
-
})
|
|
328
|
+
import { tws } from 'tailwind-to-style'
|
|
527
329
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
<
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
</Container>
|
|
536
|
-
)
|
|
537
|
-
}
|
|
330
|
+
const emailHTML = `
|
|
331
|
+
<div style="${tws('bg-white p-8 text-center', { format: 'css' })}">
|
|
332
|
+
<h1 style="${tws('text-2xl font-bold mb-4', { format: 'css' })}">
|
|
333
|
+
Welcome!
|
|
334
|
+
</h1>
|
|
335
|
+
</div>
|
|
336
|
+
`
|
|
538
337
|
```
|
|
539
338
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
### Pseudo-state Variants
|
|
543
|
-
|
|
544
|
-
Define styles for hover, focus, active, and disabled states:
|
|
339
|
+
### 3. CSS-in-JS Alternative
|
|
545
340
|
|
|
546
341
|
```javascript
|
|
547
|
-
|
|
548
|
-
base: 'w-full px-4 py-2 border border-gray-300 rounded-lg transition-all',
|
|
549
|
-
focus: 'border-blue-500 outline-none ring-2 ring-blue-200',
|
|
550
|
-
disabled: 'bg-gray-100 cursor-not-allowed opacity-60',
|
|
551
|
-
variants: {
|
|
552
|
-
error: {
|
|
553
|
-
true: 'border-red-500 focus:border-red-600 focus:ring-red-200'
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
})
|
|
342
|
+
import { twsx } from 'tailwind-to-style'
|
|
557
343
|
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
solid: 'bg-blue-600 text-white',
|
|
566
|
-
ghost: 'bg-transparent text-blue-600 border border-blue-600'
|
|
344
|
+
const styles = twsx({
|
|
345
|
+
'.app': [
|
|
346
|
+
'min-h-screen bg-gray-50',
|
|
347
|
+
{
|
|
348
|
+
'> .header': 'bg-white shadow-sm p-4',
|
|
349
|
+
'> .main': 'container mx-auto py-8',
|
|
350
|
+
'> .footer': 'bg-gray-900 text-white p-8'
|
|
567
351
|
}
|
|
568
|
-
|
|
352
|
+
]
|
|
569
353
|
})
|
|
570
354
|
|
|
571
|
-
//
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
355
|
+
// Inject once at app startup
|
|
356
|
+
document.head.appendChild(Object.assign(document.createElement('style'), {
|
|
357
|
+
textContent: styles
|
|
358
|
+
}))
|
|
575
359
|
```
|
|
576
360
|
|
|
577
|
-
###
|
|
578
|
-
|
|
579
|
-
Create complex components with nested selectors:
|
|
361
|
+
### 4. Component Library Styling
|
|
580
362
|
|
|
581
363
|
```javascript
|
|
582
|
-
|
|
583
|
-
base: 'bg-white rounded-lg shadow-md p-6',
|
|
584
|
-
nested: {
|
|
585
|
-
'.card-header': [
|
|
586
|
-
'border-b border-gray-200 pb-4 mb-4',
|
|
587
|
-
{
|
|
588
|
-
'.card-title': 'text-2xl font-bold text-gray-900',
|
|
589
|
-
'.card-subtitle': 'text-sm text-gray-500 mt-1'
|
|
590
|
-
}
|
|
591
|
-
],
|
|
592
|
-
'.card-body': 'text-gray-700 leading-relaxed',
|
|
593
|
-
'.card-footer': [
|
|
594
|
-
'border-t border-gray-200 pt-4 mt-4 flex justify-end gap-2',
|
|
595
|
-
{
|
|
596
|
-
'button': 'px-4 py-2 rounded-lg transition-colors',
|
|
597
|
-
'button.primary': 'bg-blue-500 text-white hover:bg-blue-600',
|
|
598
|
-
'button.secondary': 'bg-gray-200 text-gray-800 hover:bg-gray-300'
|
|
599
|
-
}
|
|
600
|
-
]
|
|
601
|
-
}
|
|
602
|
-
})
|
|
603
|
-
|
|
604
|
-
// Usage
|
|
605
|
-
function ProfileCard() {
|
|
606
|
-
return (
|
|
607
|
-
<Card>
|
|
608
|
-
<div className="card-header">
|
|
609
|
-
<h2 className="card-title">John Doe</h2>
|
|
610
|
-
<p className="card-subtitle">Software Engineer</p>
|
|
611
|
-
</div>
|
|
612
|
-
<div className="card-body">
|
|
613
|
-
Passionate developer with 10 years of experience...
|
|
614
|
-
</div>
|
|
615
|
-
<div className="card-footer">
|
|
616
|
-
<button className="secondary">Cancel</button>
|
|
617
|
-
<button className="primary">Save</button>
|
|
618
|
-
</div>
|
|
619
|
-
</Card>
|
|
620
|
-
)
|
|
621
|
-
}
|
|
622
|
-
```
|
|
623
|
-
|
|
624
|
-
### Compound Variants
|
|
625
|
-
|
|
626
|
-
Apply styles based on multiple variant combinations:
|
|
364
|
+
import { twsx } from 'tailwind-to-style'
|
|
627
365
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
variants: {
|
|
632
|
-
color: {
|
|
633
|
-
primary: 'bg-blue-500 text-white',
|
|
634
|
-
secondary: 'bg-gray-500 text-white'
|
|
635
|
-
},
|
|
636
|
-
size: {
|
|
637
|
-
sm: 'text-sm',
|
|
638
|
-
lg: 'text-lg'
|
|
639
|
-
},
|
|
640
|
-
outlined: {
|
|
641
|
-
true: 'bg-transparent border-2'
|
|
642
|
-
}
|
|
643
|
-
},
|
|
644
|
-
compoundVariants: [
|
|
645
|
-
{
|
|
646
|
-
color: 'primary',
|
|
647
|
-
outlined: true,
|
|
648
|
-
class: 'border-blue-500 text-blue-500 hover:bg-blue-50'
|
|
649
|
-
},
|
|
650
|
-
{
|
|
651
|
-
color: 'secondary',
|
|
652
|
-
outlined: true,
|
|
653
|
-
class: 'border-gray-500 text-gray-500 hover:bg-gray-50'
|
|
654
|
-
},
|
|
366
|
+
export const buttonStyles = twsx({
|
|
367
|
+
'.btn': [
|
|
368
|
+
'px-4 py-2 rounded-lg font-medium transition-colors',
|
|
655
369
|
{
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
370
|
+
'&.btn-primary': 'bg-blue-500 text-white hover:bg-blue-600',
|
|
371
|
+
'&.btn-secondary': 'bg-gray-200 text-gray-900 hover:bg-gray-300',
|
|
372
|
+
'&.btn-lg': 'px-6 py-3 text-lg',
|
|
373
|
+
'&.btn-sm': 'px-2 py-1 text-sm'
|
|
659
374
|
}
|
|
660
375
|
]
|
|
661
376
|
})
|
|
662
|
-
|
|
663
|
-
// Usage
|
|
664
|
-
<Button color="primary" outlined>Outlined Primary</Button>
|
|
665
|
-
<Button color="secondary" size="lg" outlined>Large Outlined</Button>
|
|
666
377
|
```
|
|
667
378
|
|
|
668
|
-
|
|
379
|
+
## ð§ Advanced Features
|
|
669
380
|
|
|
670
|
-
|
|
381
|
+
### Responsive Design
|
|
671
382
|
|
|
672
|
-
|
|
673
|
-
const Button = styled('button', {
|
|
674
|
-
base: 'px-4 py-2 rounded-lg bg-blue-500 text-white font-medium'
|
|
675
|
-
})
|
|
383
|
+
All Tailwind breakpoints work out of the box:
|
|
676
384
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
<>
|
|
680
|
-
{/* Renders as <button> */}
|
|
681
|
-
<Button onClick={() => alert('Clicked!')}>
|
|
682
|
-
Click Me
|
|
683
|
-
</Button>
|
|
684
|
-
|
|
685
|
-
{/* Renders as <a> with same styles */}
|
|
686
|
-
<Button as="a" href="/signup">
|
|
687
|
-
Sign Up
|
|
688
|
-
</Button>
|
|
689
|
-
|
|
690
|
-
{/* Renders as custom component */}
|
|
691
|
-
<Button as={Link} to="/dashboard">
|
|
692
|
-
Dashboard
|
|
693
|
-
</Button>
|
|
694
|
-
</>
|
|
695
|
-
)
|
|
696
|
-
}
|
|
385
|
+
```javascript
|
|
386
|
+
const styles = tws('text-sm sm:text-base md:text-lg lg:text-xl xl:text-2xl')
|
|
697
387
|
```
|
|
698
388
|
|
|
699
|
-
###
|
|
389
|
+
### Arbitrary Values
|
|
700
390
|
|
|
701
|
-
|
|
391
|
+
Use any custom value with square brackets:
|
|
702
392
|
|
|
703
393
|
```javascript
|
|
704
|
-
const
|
|
705
|
-
|
|
706
|
-
|
|
394
|
+
const styles = tws(`
|
|
395
|
+
w-[123px]
|
|
396
|
+
h-[calc(100vh-64px)]
|
|
397
|
+
text-[#abc123]
|
|
398
|
+
bg-[url('/image.png')]
|
|
399
|
+
grid-cols-[1fr,2fr,1fr]
|
|
400
|
+
`)
|
|
401
|
+
```
|
|
707
402
|
|
|
708
|
-
|
|
709
|
-
base: 'bg-blue-500 text-white hover:bg-blue-600'
|
|
710
|
-
})
|
|
403
|
+
### State Variants
|
|
711
404
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
405
|
+
```javascript
|
|
406
|
+
const styles = tws(`
|
|
407
|
+
bg-blue-500
|
|
408
|
+
hover:bg-blue-600
|
|
409
|
+
focus:ring-2
|
|
410
|
+
active:scale-95
|
|
411
|
+
disabled:opacity-50
|
|
412
|
+
group-hover:text-white
|
|
413
|
+
`)
|
|
414
|
+
```
|
|
718
415
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
416
|
+
### Important Modifier
|
|
417
|
+
|
|
418
|
+
```javascript
|
|
419
|
+
const styles = tws('!bg-red-500 !text-white')
|
|
420
|
+
// Forces these styles to take precedence
|
|
724
421
|
```
|
|
725
422
|
|
|
726
|
-
|
|
423
|
+
## ð Framework Integration
|
|
727
424
|
|
|
728
|
-
|
|
425
|
+
### React
|
|
729
426
|
|
|
730
427
|
```javascript
|
|
731
|
-
import {
|
|
428
|
+
import { tws, twsx } from 'tailwind-to-style'
|
|
429
|
+
import { useEffect } from 'react'
|
|
732
430
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
size: {
|
|
743
|
-
sm: 'text-sm px-3 py-1.5',
|
|
744
|
-
md: 'text-base px-4 py-2',
|
|
745
|
-
lg: 'text-lg px-6 py-3'
|
|
746
|
-
},
|
|
747
|
-
fullWidth: {
|
|
748
|
-
true: 'w-full'
|
|
749
|
-
}
|
|
750
|
-
},
|
|
751
|
-
compoundVariants: [
|
|
752
|
-
{
|
|
753
|
-
intent: 'primary',
|
|
754
|
-
size: 'lg',
|
|
755
|
-
class: 'shadow-lg hover:shadow-xl'
|
|
756
|
-
}
|
|
757
|
-
],
|
|
758
|
-
defaultVariants: {
|
|
759
|
-
intent: 'primary',
|
|
760
|
-
size: 'md'
|
|
761
|
-
}
|
|
762
|
-
})
|
|
431
|
+
function App() {
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
const css = twsx({
|
|
434
|
+
'.custom': 'bg-blue-500 text-white p-4'
|
|
435
|
+
})
|
|
436
|
+
const style = document.createElement('style')
|
|
437
|
+
style.textContent = css
|
|
438
|
+
document.head.appendChild(style)
|
|
439
|
+
}, [])
|
|
763
440
|
|
|
764
|
-
// Use in React
|
|
765
|
-
function Button({ intent, size, fullWidth, children, ...props }) {
|
|
766
441
|
return (
|
|
767
|
-
<
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
</button>
|
|
442
|
+
<div style={tws('flex items-center gap-4')}>
|
|
443
|
+
<button style={tws('bg-blue-500 text-white px-4 py-2 rounded')}>
|
|
444
|
+
Click me
|
|
445
|
+
</button>
|
|
446
|
+
</div>
|
|
773
447
|
)
|
|
774
448
|
}
|
|
449
|
+
```
|
|
775
450
|
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
451
|
+
### Vue
|
|
452
|
+
|
|
453
|
+
```vue
|
|
454
|
+
<script setup>
|
|
455
|
+
import { tws } from 'tailwind-to-style'
|
|
456
|
+
|
|
457
|
+
const buttonStyle = tws('bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600')
|
|
458
|
+
</script>
|
|
779
459
|
|
|
780
|
-
// Use in Vue
|
|
781
460
|
<template>
|
|
782
|
-
<button :
|
|
783
|
-
Click me
|
|
784
|
-
</button>
|
|
461
|
+
<button :style="buttonStyle">Click me</button>
|
|
785
462
|
</template>
|
|
786
463
|
```
|
|
787
464
|
|
|
788
|
-
###
|
|
465
|
+
### Vanilla JS
|
|
789
466
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
#### Global Configuration
|
|
467
|
+
```javascript
|
|
468
|
+
import { tws, twsx } from 'tailwind-to-style'
|
|
793
469
|
|
|
794
|
-
|
|
470
|
+
// Create element with styles
|
|
471
|
+
const button = document.createElement('button')
|
|
472
|
+
Object.assign(button.style, tws('bg-blue-500 text-white px-4 py-2 rounded'))
|
|
473
|
+
button.textContent = 'Click me'
|
|
795
474
|
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
configure({
|
|
800
|
-
styled: {
|
|
801
|
-
prefix: 'myapp', // Default: 'twsx'
|
|
802
|
-
separator: '_', // Default: '-'
|
|
803
|
-
hashLength: 5, // Default: 6
|
|
804
|
-
includeComponentName: true // Default: true
|
|
805
|
-
}
|
|
806
|
-
})
|
|
807
|
-
|
|
808
|
-
const Button = styled('button', {
|
|
809
|
-
base: 'px-4 py-2 rounded-lg'
|
|
810
|
-
})
|
|
811
|
-
// Generates: myapp_button_a3k9x (instead of twsx-button-a3k9x2)
|
|
812
|
-
// Variants: myapp_color_primary (instead of twsx-color-primary)
|
|
813
|
-
```
|
|
814
|
-
|
|
815
|
-
#### Per-Component Override
|
|
816
|
-
|
|
817
|
-
Override naming for specific components:
|
|
818
|
-
|
|
819
|
-
```javascript
|
|
820
|
-
const CustomButton = styled('button',
|
|
821
|
-
{
|
|
822
|
-
base: 'px-4 py-2 rounded-lg',
|
|
823
|
-
variants: {
|
|
824
|
-
variant: {
|
|
825
|
-
solid: 'bg-blue-500',
|
|
826
|
-
outline: 'border border-blue-500'
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
},
|
|
830
|
-
{
|
|
831
|
-
// Component-specific naming options
|
|
832
|
-
prefix: 'btn',
|
|
833
|
-
separator: '-',
|
|
834
|
-
hashLength: 8,
|
|
835
|
-
includeComponentName: false
|
|
836
|
-
}
|
|
837
|
-
)
|
|
838
|
-
// Generates: btn-a3k9x2f1 (no component type)
|
|
839
|
-
// Variants: btn-variant-solid
|
|
840
|
-
```
|
|
841
|
-
|
|
842
|
-
#### Use Cases
|
|
843
|
-
|
|
844
|
-
**1. Brand-specific Prefix:**
|
|
845
|
-
```javascript
|
|
846
|
-
configure({ styled: { prefix: 'shopify' }})
|
|
847
|
-
// shopify-button-a3k9x2
|
|
848
|
-
```
|
|
849
|
-
|
|
850
|
-
**2. Minimal Classnames:**
|
|
851
|
-
```javascript
|
|
852
|
-
configure({
|
|
853
|
-
styled: {
|
|
854
|
-
prefix: 'c',
|
|
855
|
-
separator: '',
|
|
856
|
-
hashLength: 4,
|
|
857
|
-
includeComponentName: false
|
|
858
|
-
}
|
|
859
|
-
})
|
|
860
|
-
// c1a2b (super compact!)
|
|
861
|
-
```
|
|
862
|
-
|
|
863
|
-
**3. Monorepo Isolation:**
|
|
864
|
-
```javascript
|
|
865
|
-
// Admin app
|
|
866
|
-
configure({ styled: { prefix: 'admin' }})
|
|
867
|
-
// admin-button-xxx
|
|
868
|
-
|
|
869
|
-
// Customer portal
|
|
870
|
-
configure({ styled: { prefix: 'portal' }})
|
|
871
|
-
// portal-button-yyy
|
|
872
|
-
```
|
|
873
|
-
|
|
874
|
-
**4. BEM-like Convention:**
|
|
875
|
-
```javascript
|
|
876
|
-
configure({ styled: { separator: '__' }})
|
|
877
|
-
// twsx__button__a3k9x2
|
|
878
|
-
```
|
|
879
|
-
|
|
880
|
-
**5. Design System:**
|
|
881
|
-
```javascript
|
|
882
|
-
configure({ styled: { prefix: 'ds' }})
|
|
883
|
-
const Button = styled('button', config)
|
|
884
|
-
const Card = styled('div', config)
|
|
885
|
-
// ds-button-a3k9x2, ds-card-f8d2k1
|
|
886
|
-
```
|
|
887
|
-
|
|
888
|
-
#### HTML Output Examples
|
|
889
|
-
|
|
890
|
-
```html
|
|
891
|
-
<!-- Default (prefix: 'twsx', separator: '-') -->
|
|
892
|
-
<button class="twsx-button-a3k9x2 twsx-color-primary">Click</button>
|
|
893
|
-
|
|
894
|
-
<!-- Custom (prefix: 'myapp', separator: '_') -->
|
|
895
|
-
<button class="myapp_button_a3k9 myapp_color_primary">Click</button>
|
|
896
|
-
|
|
897
|
-
<!-- Minimal (prefix: 'c', no component name) -->
|
|
898
|
-
<button class="c-1a2b c-color-primary">Click</button>
|
|
899
|
-
|
|
900
|
-
<!-- BEM-like (separator: '__') -->
|
|
901
|
-
<button class="twsx__button__a3k9x2 twsx__color__primary">Click</button>
|
|
902
|
-
```
|
|
903
|
-
|
|
904
|
-
**[ð Full Example â](examples/custom-prefix.js)**
|
|
905
|
-
|
|
906
|
-
### Batch Variant Creation
|
|
907
|
-
|
|
908
|
-
Create multiple variant functions at once:
|
|
909
|
-
|
|
910
|
-
```javascript
|
|
911
|
-
import { createVariants } from 'tailwind-to-style'
|
|
912
|
-
|
|
913
|
-
const components = createVariants({
|
|
914
|
-
button: {
|
|
915
|
-
base: 'px-4 py-2 rounded font-medium',
|
|
916
|
-
variants: {
|
|
917
|
-
color: {
|
|
918
|
-
primary: 'bg-blue-500 text-white',
|
|
919
|
-
secondary: 'bg-gray-500 text-white'
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
},
|
|
923
|
-
badge: {
|
|
924
|
-
base: 'px-2 py-1 text-xs rounded-full font-semibold',
|
|
925
|
-
variants: {
|
|
926
|
-
color: {
|
|
927
|
-
success: 'bg-green-100 text-green-800',
|
|
928
|
-
error: 'bg-red-100 text-red-800',
|
|
929
|
-
warning: 'bg-yellow-100 text-yellow-800'
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
},
|
|
933
|
-
input: {
|
|
934
|
-
base: 'w-full px-3 py-2 border rounded',
|
|
935
|
-
variants: {
|
|
936
|
-
error: {
|
|
937
|
-
true: 'border-red-500',
|
|
938
|
-
false: 'border-gray-300'
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
})
|
|
943
|
-
|
|
944
|
-
// Use the variants
|
|
945
|
-
const buttonClass = components.button({ color: 'primary' })
|
|
946
|
-
const badgeClass = components.badge({ color: 'success' })
|
|
947
|
-
const inputClass = components.input({ error: true })
|
|
948
|
-
```
|
|
949
|
-
|
|
950
|
-
### TypeScript Support
|
|
951
|
-
|
|
952
|
-
Full type inference for variants and props:
|
|
953
|
-
|
|
954
|
-
```typescript
|
|
955
|
-
import { styled, tv } from 'tailwind-to-style/react'
|
|
956
|
-
import type { StyledProps } from 'tailwind-to-style/react'
|
|
957
|
-
|
|
958
|
-
// Styled components have inferred types
|
|
959
|
-
const Button = styled('button', {
|
|
960
|
-
variants: {
|
|
961
|
-
color: {
|
|
962
|
-
primary: 'bg-blue-500',
|
|
963
|
-
secondary: 'bg-gray-500'
|
|
964
|
-
},
|
|
965
|
-
size: {
|
|
966
|
-
sm: 'text-sm',
|
|
967
|
-
lg: 'text-lg'
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
})
|
|
971
|
-
|
|
972
|
-
// Props are fully typed
|
|
973
|
-
function App() {
|
|
974
|
-
return (
|
|
975
|
-
<>
|
|
976
|
-
<Button color="primary" size="lg">Typed!</Button>
|
|
977
|
-
{/* @ts-expect-error - invalid variant */}
|
|
978
|
-
<Button color="invalid">Error</Button>
|
|
979
|
-
</>
|
|
980
|
-
)
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// Extract prop types
|
|
984
|
-
type ButtonProps = StyledProps<typeof Button>
|
|
985
|
-
// { color?: 'primary' | 'secondary', size?: 'sm' | 'lg', as?: any, ... }
|
|
986
|
-
|
|
987
|
-
// tv() also has full type inference
|
|
988
|
-
const cardVariants = tv({
|
|
989
|
-
variants: {
|
|
990
|
-
elevated: {
|
|
991
|
-
true: 'shadow-lg',
|
|
992
|
-
false: 'shadow-none'
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
})
|
|
996
|
-
|
|
997
|
-
// Type-safe variant props
|
|
998
|
-
const className = cardVariants({ elevated: true }) // â
|
|
999
|
-
const invalid = cardVariants({ elevated: 'yes' }) // â Type error
|
|
1000
|
-
```
|
|
1001
|
-
|
|
1002
|
-
### Performance & Class Naming
|
|
1003
|
-
|
|
1004
|
-
#### Deterministic Hash-based Class Names
|
|
1005
|
-
|
|
1006
|
-
`styled()` components generate **deterministic class names** based on the component's configuration. This ensures:
|
|
1007
|
-
|
|
1008
|
-
- **Stability**: Same config = same class name across all renders
|
|
1009
|
-
- **SSR Compatibility**: Server and client generate identical class names, preventing hydration mismatches
|
|
1010
|
-
- **Predictability**: Class names are consistent across environments and builds
|
|
1011
|
-
|
|
1012
|
-
```javascript
|
|
1013
|
-
const Button = styled('button', {
|
|
1014
|
-
base: 'px-4 py-2 rounded bg-blue-500'
|
|
1015
|
-
})
|
|
1016
|
-
// Generates: twsx-button-a1b2c3 (deterministic hash)
|
|
1017
|
-
```
|
|
1018
|
-
|
|
1019
|
-
#### Optimized CSS Injection
|
|
1020
|
-
|
|
1021
|
-
The library uses a **global CSS cache** with hash-based deduplication:
|
|
1022
|
-
|
|
1023
|
-
- **Single Style Tag**: All component styles are injected into one `<style data-twsx-global>` tag
|
|
1024
|
-
- **No Duplicate CSS**: Same styles are cached and reused across components
|
|
1025
|
-
- **Efficient Updates**: Only new styles are added, existing styles are preserved
|
|
1026
|
-
- **Memory Efficient**: CSS content hashing prevents redundant injections
|
|
1027
|
-
|
|
1028
|
-
```javascript
|
|
1029
|
-
// Multiple instances share the same cached styles
|
|
1030
|
-
function App() {
|
|
1031
|
-
return (
|
|
1032
|
-
<>
|
|
1033
|
-
<Button>First</Button> {/* Injects CSS */}
|
|
1034
|
-
<Button>Second</Button> {/* Reuses cached CSS */}
|
|
1035
|
-
<Button>Third</Button> {/* Reuses cached CSS */}
|
|
1036
|
-
</>
|
|
1037
|
-
)
|
|
1038
|
-
}
|
|
1039
|
-
```
|
|
1040
|
-
|
|
1041
|
-
#### Best Practices
|
|
1042
|
-
|
|
1043
|
-
1. **Define components outside render** - Component definitions should be at module level:
|
|
1044
|
-
```javascript
|
|
1045
|
-
// â
Good - defined once at module level
|
|
1046
|
-
const Button = styled('button', { base: 'px-4 py-2' })
|
|
1047
|
-
|
|
1048
|
-
function App() {
|
|
1049
|
-
return <Button>Click</Button>
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
// â Bad - redefined on every render
|
|
1053
|
-
function App() {
|
|
1054
|
-
const Button = styled('button', { base: 'px-4 py-2' })
|
|
1055
|
-
return <Button>Click</Button>
|
|
1056
|
-
}
|
|
1057
|
-
```
|
|
1058
|
-
|
|
1059
|
-
2. **Use `tv()` for dynamic variants** - When you need runtime flexibility:
|
|
1060
|
-
```javascript
|
|
1061
|
-
const buttonVariants = tv({
|
|
1062
|
-
variants: { color: { primary: '...', secondary: '...' } }
|
|
475
|
+
// Inject global styles
|
|
476
|
+
const css = twsx({
|
|
477
|
+
'.card': 'bg-white p-6 rounded-lg shadow-md'
|
|
1063
478
|
})
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
}
|
|
1068
|
-
```
|
|
1069
|
-
|
|
1070
|
-
3. **SSR Considerations** - Class names are deterministic, ensuring proper hydration:
|
|
1071
|
-
```javascript
|
|
1072
|
-
// Server renders: <button class="twsx-button-a1b2c3">
|
|
1073
|
-
// Client hydrates: <button class="twsx-button-a1b2c3"> â
No mismatch!
|
|
1074
|
-
```
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
### 1. `tws`
|
|
1079
|
-
|
|
1080
|
-
The `tws` function is designed to convert Tailwind CSS utility classes into either **inline CSS** or **JSON style objects**. This makes it particularly useful for applying styles dynamically in React or similar frameworks where inline styles or style objects are often needed.
|
|
1081
|
-
|
|
1082
|
-
#### Features of `tws`:
|
|
1083
|
-
|
|
1084
|
-
- Converts Tailwind utility classes into **inline CSS** or **JSON style objects**.
|
|
1085
|
-
|
|
1086
|
-
#### Usage
|
|
1087
|
-
|
|
1088
|
-
```javascript
|
|
1089
|
-
import { tws } from "tailwind-to-style";
|
|
1090
|
-
|
|
1091
|
-
// Convert Tailwind classes to inline CSS
|
|
1092
|
-
const styleInline = tws("bg-white mx-auto");
|
|
1093
|
-
// Output: background-color:rgba(255, 255, 255, 1); margin-left:auto; margin-right:auto;
|
|
1094
|
-
|
|
1095
|
-
// Convert Tailwind classes to JSON style object
|
|
1096
|
-
const styleJSON = tws("bg-white mx-auto", 1);
|
|
1097
|
-
// Output: { backgroundColor: 'rgba(255, 255, 255, 1)', marginLeft: 'auto', marginRight: 'auto' }
|
|
1098
|
-
```
|
|
1099
|
-
|
|
1100
|
-
- **First argument**: The string of Tailwind classes to convert.
|
|
1101
|
-
- **Second argument (optional)**: Pass `1` to get the result as a JSON object (default is inline CSS when omitted).
|
|
1102
|
-
|
|
1103
|
-
#### Example in React:
|
|
1104
|
-
|
|
1105
|
-
```javascript
|
|
1106
|
-
import React from "react";
|
|
1107
|
-
import { tws } from "tailwind-to-style";
|
|
1108
|
-
|
|
1109
|
-
const App = () => {
|
|
1110
|
-
return (
|
|
1111
|
-
<div style={tws("text-red-500 bg-blue-200 p-4", 1)}>
|
|
1112
|
-
Hello, this is styled using tailwind-to-style
|
|
1113
|
-
</div>
|
|
1114
|
-
);
|
|
1115
|
-
};
|
|
1116
|
-
|
|
1117
|
-
export default App;
|
|
1118
|
-
```
|
|
1119
|
-
|
|
1120
|
-
This will apply the Tailwind classes directly as inline styles in the React component.
|
|
1121
|
-
|
|
1122
|
-
## Advanced Features (v2.10.0+)
|
|
1123
|
-
|
|
1124
|
-
### Logger Configuration
|
|
1125
|
-
|
|
1126
|
-
Control logging behavior for production environments:
|
|
1127
|
-
|
|
1128
|
-
```javascript
|
|
1129
|
-
import { logger } from "tailwind-to-style";
|
|
1130
|
-
|
|
1131
|
-
// Set log level (debug, info, warn, error, silent)
|
|
1132
|
-
logger.setLevel('error'); // Only show errors in production
|
|
1133
|
-
|
|
1134
|
-
// Default is 'warn' in development, 'error' in production
|
|
1135
|
-
```
|
|
1136
|
-
|
|
1137
|
-
### Error Handling
|
|
1138
|
-
|
|
1139
|
-
Subscribe to errors for monitoring and debugging:
|
|
1140
|
-
|
|
1141
|
-
```javascript
|
|
1142
|
-
import { onError, TwsError } from "tailwind-to-style";
|
|
1143
|
-
|
|
1144
|
-
// Subscribe to errors
|
|
1145
|
-
const unsubscribe = onError((error) => {
|
|
1146
|
-
console.log(error.message);
|
|
1147
|
-
console.log(error.context); // Additional context
|
|
1148
|
-
console.log(error.timestamp); // When it occurred
|
|
1149
|
-
|
|
1150
|
-
// Send to error tracking service
|
|
1151
|
-
// Sentry.captureException(error);
|
|
1152
|
-
});
|
|
1153
|
-
|
|
1154
|
-
// Unsubscribe when done
|
|
1155
|
-
unsubscribe();
|
|
1156
|
-
```
|
|
1157
|
-
|
|
1158
|
-
### Cache Management
|
|
1159
|
-
|
|
1160
|
-
For testing or memory management:
|
|
1161
|
-
|
|
1162
|
-
```javascript
|
|
1163
|
-
import { getTailwindCache, resetTailwindCache } from "tailwind-to-style";
|
|
1164
|
-
|
|
1165
|
-
// Get cache instance
|
|
1166
|
-
const cache = getTailwindCache();
|
|
1167
|
-
|
|
1168
|
-
// Check if initialized
|
|
1169
|
-
if (cache.isInitialized()) {
|
|
1170
|
-
console.log("Cache is ready");
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
// Reset cache (useful for testing)
|
|
1174
|
-
resetTailwindCache();
|
|
1175
|
-
```
|
|
1176
|
-
|
|
1177
|
-
### Custom Logger Instance
|
|
1178
|
-
|
|
1179
|
-
Create your own logger with custom settings:
|
|
1180
|
-
|
|
1181
|
-
```javascript
|
|
1182
|
-
import { Logger } from "tailwind-to-style";
|
|
1183
|
-
|
|
1184
|
-
const customLogger = new Logger('debug');
|
|
1185
|
-
customLogger.debug('Custom message');
|
|
1186
|
-
customLogger.setLevel('silent'); // Disable all logging
|
|
1187
|
-
```
|
|
1188
|
-
|
|
1189
|
-
## Animations & Transitions (v2.10.0+)
|
|
1190
|
-
|
|
1191
|
-
Full support for Tailwind CSS animations and transitions!
|
|
1192
|
-
|
|
1193
|
-
### Built-in Animations
|
|
1194
|
-
|
|
1195
|
-
```javascript
|
|
1196
|
-
import { tws } from "tailwind-to-style";
|
|
1197
|
-
|
|
1198
|
-
// Spin animation (loading spinners)
|
|
1199
|
-
const spinner = tws("animate-spin", 1);
|
|
1200
|
-
// { animation: "spin 1s linear infinite" }
|
|
1201
|
-
|
|
1202
|
-
// Ping animation (notification badges)
|
|
1203
|
-
const badge = tws("animate-ping", 1);
|
|
1204
|
-
// { animation: "ping 1s cubic-bezier(0, 0, 0.2, 1) infinite" }
|
|
1205
|
-
|
|
1206
|
-
// Pulse animation (breathing effect)
|
|
1207
|
-
const pulse = tws("animate-pulse", 1);
|
|
1208
|
-
// { animation: "pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite" }
|
|
1209
|
-
|
|
1210
|
-
// Bounce animation
|
|
1211
|
-
const bounce = tws("animate-bounce", 1);
|
|
1212
|
-
// { animation: "bounce 1s infinite" }
|
|
1213
|
-
|
|
1214
|
-
// Disable animation
|
|
1215
|
-
const none = tws("animate-none", 1);
|
|
1216
|
-
// { animation: "none" }
|
|
1217
|
-
```
|
|
1218
|
-
|
|
1219
|
-
### Transition Utilities
|
|
1220
|
-
|
|
1221
|
-
```javascript
|
|
1222
|
-
// Basic transition
|
|
1223
|
-
const button = tws("transition duration-300 ease-in-out", 1);
|
|
1224
|
-
// {
|
|
1225
|
-
// transitionProperty: "color, background-color, border-color, ...",
|
|
1226
|
-
// transitionDuration: "300ms",
|
|
1227
|
-
// transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)"
|
|
1228
|
-
// }
|
|
1229
|
-
|
|
1230
|
-
// Transition specific properties
|
|
1231
|
-
tws("transition-colors", 1); // Only colors
|
|
1232
|
-
tws("transition-opacity", 1); // Only opacity
|
|
1233
|
-
tws("transition-shadow", 1); // Only box-shadow
|
|
1234
|
-
tws("transition-transform", 1); // Only transform
|
|
1235
|
-
tws("transition-all", 1); // All properties
|
|
1236
|
-
|
|
1237
|
-
// Duration
|
|
1238
|
-
tws("duration-75", 1); // 75ms
|
|
1239
|
-
tws("duration-150", 1); // 150ms
|
|
1240
|
-
tws("duration-300", 1); // 300ms
|
|
1241
|
-
tws("duration-500", 1); // 500ms
|
|
1242
|
-
tws("duration-1000", 1); // 1000ms
|
|
1243
|
-
|
|
1244
|
-
// Timing functions
|
|
1245
|
-
tws("ease-linear", 1); // linear
|
|
1246
|
-
tws("ease-in", 1); // cubic-bezier(0.4, 0, 1, 1)
|
|
1247
|
-
tws("ease-out", 1); // cubic-bezier(0, 0, 0.2, 1)
|
|
1248
|
-
tws("ease-in-out", 1); // cubic-bezier(0.4, 0, 0.2, 1)
|
|
1249
|
-
|
|
1250
|
-
// Delay
|
|
1251
|
-
tws("delay-75", 1); // 75ms
|
|
1252
|
-
tws("delay-150", 1); // 150ms
|
|
1253
|
-
tws("delay-300", 1); // 300ms
|
|
1254
|
-
tws("delay-500", 1); // 500ms
|
|
1255
|
-
```
|
|
1256
|
-
|
|
1257
|
-
### Custom Animations
|
|
1258
|
-
|
|
1259
|
-
Create your own animations using `configure()`:
|
|
1260
|
-
|
|
1261
|
-
```javascript
|
|
1262
|
-
import { configure, tws } from "tailwind-to-style";
|
|
1263
|
-
|
|
1264
|
-
configure({
|
|
1265
|
-
theme: {
|
|
1266
|
-
extend: {
|
|
1267
|
-
animation: {
|
|
1268
|
-
'fade-in': 'fadeIn 1s ease-in forwards',
|
|
1269
|
-
'slide-up': 'slideUp 0.5s ease-out',
|
|
1270
|
-
'wiggle': 'wiggle 1s ease-in-out infinite',
|
|
1271
|
-
},
|
|
1272
|
-
keyframes: {
|
|
1273
|
-
fadeIn: {
|
|
1274
|
-
'0%': { opacity: '0' },
|
|
1275
|
-
'100%': { opacity: '1' },
|
|
1276
|
-
},
|
|
1277
|
-
slideUp: {
|
|
1278
|
-
'0%': { transform: 'translateY(100%)' },
|
|
1279
|
-
'100%': { transform: 'translateY(0)' },
|
|
1280
|
-
},
|
|
1281
|
-
wiggle: {
|
|
1282
|
-
'0%, 100%': { transform: 'rotate(-3deg)' },
|
|
1283
|
-
'50%': { transform: 'rotate(3deg)' },
|
|
1284
|
-
},
|
|
1285
|
-
},
|
|
1286
|
-
},
|
|
1287
|
-
},
|
|
1288
|
-
});
|
|
1289
|
-
|
|
1290
|
-
// Use custom animations
|
|
1291
|
-
const modal = tws("animate-fade-in", 1);
|
|
1292
|
-
// { animation: "fadeIn 1s ease-in forwards" }
|
|
1293
|
-
|
|
1294
|
-
const notification = tws("animate-slide-up", 1);
|
|
1295
|
-
// { animation: "slideUp 0.5s ease-out" }
|
|
1296
|
-
```
|
|
1297
|
-
|
|
1298
|
-
### Real-World Examples
|
|
1299
|
-
|
|
1300
|
-
```javascript
|
|
1301
|
-
// Button with hover transition
|
|
1302
|
-
const button = tws(
|
|
1303
|
-
"bg-blue-500 hover:bg-blue-600 transition-colors duration-200",
|
|
1304
|
-
1
|
|
1305
|
-
);
|
|
1306
|
-
|
|
1307
|
-
// Loading spinner
|
|
1308
|
-
const spinner = tws(
|
|
1309
|
-
"animate-spin w-8 h-8 border-2 border-blue-500 rounded-full",
|
|
1310
|
-
1
|
|
1311
|
-
);
|
|
1312
|
-
|
|
1313
|
-
// Notification with fade in
|
|
1314
|
-
const notification = tws(
|
|
1315
|
-
"animate-fade-in bg-green-500 text-white p-4 rounded shadow-lg",
|
|
1316
|
-
1
|
|
1317
|
-
);
|
|
1318
|
-
|
|
1319
|
-
// Menu with slide transition
|
|
1320
|
-
const menu = tws(
|
|
1321
|
-
"transition-transform duration-300 ease-out transform translate-x-0",
|
|
1322
|
-
1
|
|
1323
|
-
);
|
|
1324
|
-
|
|
1325
|
-
// Icon with hover wiggle
|
|
1326
|
-
const icon = tws("hover:animate-wiggle cursor-pointer", 1);
|
|
1327
|
-
```
|
|
1328
|
-
|
|
1329
|
-
See [examples/animations.js](examples/animations.js) for more examples!
|
|
1330
|
-
|
|
1331
|
-
## Theme Customization (v2.10.0+)
|
|
1332
|
-
|
|
1333
|
-
Extend the default Tailwind theme with your own custom values:
|
|
1334
|
-
|
|
1335
|
-
### Basic Theme Extension
|
|
1336
|
-
|
|
1337
|
-
```javascript
|
|
1338
|
-
import { configure, tws } from "tailwind-to-style";
|
|
1339
|
-
|
|
1340
|
-
// Configure custom theme
|
|
1341
|
-
configure({
|
|
1342
|
-
theme: {
|
|
1343
|
-
extend: {
|
|
1344
|
-
colors: {
|
|
1345
|
-
brand: {
|
|
1346
|
-
500: '#0ea5e9',
|
|
1347
|
-
600: '#0284c7',
|
|
1348
|
-
},
|
|
1349
|
-
accent: '#ff6b6b',
|
|
1350
|
-
},
|
|
1351
|
-
spacing: {
|
|
1352
|
-
'128': '32rem',
|
|
1353
|
-
'144': '36rem',
|
|
1354
|
-
},
|
|
1355
|
-
borderRadius: {
|
|
1356
|
-
'4xl': '2rem',
|
|
1357
|
-
},
|
|
1358
|
-
},
|
|
1359
|
-
},
|
|
1360
|
-
});
|
|
1361
|
-
|
|
1362
|
-
// Now use your custom values
|
|
1363
|
-
const button = tws('bg-brand-500 hover:bg-brand-600 p-128 rounded-4xl', 1);
|
|
1364
|
-
// Works! Uses your custom colors, spacing, and border radius
|
|
1365
|
-
```
|
|
1366
|
-
|
|
1367
|
-
### Theme Configuration Options
|
|
1368
|
-
|
|
1369
|
-
```javascript
|
|
1370
|
-
configure({
|
|
1371
|
-
theme: {
|
|
1372
|
-
extend: {
|
|
1373
|
-
colors: { /* custom colors */ },
|
|
1374
|
-
spacing: { /* custom spacing */ },
|
|
1375
|
-
borderRadius: { /* custom border radius */ },
|
|
1376
|
-
fontSize: { /* custom font sizes */ },
|
|
1377
|
-
// ... any Tailwind theme property
|
|
1378
|
-
},
|
|
1379
|
-
},
|
|
1380
|
-
|
|
1381
|
-
// Optional: Add prefix to all classes
|
|
1382
|
-
prefix: 'tw-',
|
|
1383
|
-
|
|
1384
|
-
// Optional: Disable specific core plugins
|
|
1385
|
-
corePlugins: {
|
|
1386
|
-
float: false,
|
|
1387
|
-
clear: false,
|
|
1388
|
-
},
|
|
1389
|
-
});
|
|
479
|
+
document.head.appendChild(Object.assign(document.createElement('style'), {
|
|
480
|
+
textContent: css
|
|
481
|
+
}))
|
|
1390
482
|
```
|
|
1391
483
|
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
Create `tailwind-to-style.config.js`:
|
|
1395
|
-
|
|
1396
|
-
```javascript
|
|
1397
|
-
export default {
|
|
1398
|
-
theme: {
|
|
1399
|
-
extend: {
|
|
1400
|
-
colors: {
|
|
1401
|
-
brand: {
|
|
1402
|
-
500: '#0ea5e9',
|
|
1403
|
-
},
|
|
1404
|
-
},
|
|
1405
|
-
},
|
|
1406
|
-
},
|
|
1407
|
-
// Styled components configuration
|
|
1408
|
-
styled: {
|
|
1409
|
-
prefix: 'myapp', // Customize classname prefix
|
|
1410
|
-
separator: '-', // Customize separator
|
|
1411
|
-
hashLength: 6, // Hash length (4-8 recommended)
|
|
1412
|
-
includeComponentName: true // Include component type in classname
|
|
1413
|
-
}
|
|
1414
|
-
};
|
|
1415
|
-
```
|
|
1416
|
-
|
|
1417
|
-
Then import and use it:
|
|
1418
|
-
|
|
1419
|
-
```javascript
|
|
1420
|
-
import { configure } from "tailwind-to-style";
|
|
1421
|
-
import config from "./tailwind-to-style.config.js";
|
|
1422
|
-
|
|
1423
|
-
configure(config);
|
|
1424
|
-
```
|
|
1425
|
-
|
|
1426
|
-
### Configuration Options
|
|
1427
|
-
|
|
1428
|
-
```javascript
|
|
1429
|
-
configure({
|
|
1430
|
-
// Theme customization
|
|
1431
|
-
theme: {
|
|
1432
|
-
extend: {
|
|
1433
|
-
colors: { /* custom colors */ },
|
|
1434
|
-
spacing: { /* custom spacing */ },
|
|
1435
|
-
borderRadius: { /* custom border radius */ },
|
|
1436
|
-
fontSize: { /* custom font sizes */ },
|
|
1437
|
-
// ... any Tailwind theme property
|
|
1438
|
-
},
|
|
1439
|
-
},
|
|
1440
|
-
|
|
1441
|
-
// Optional: Add prefix to utility classes
|
|
1442
|
-
prefix: 'tw-',
|
|
1443
|
-
|
|
1444
|
-
// Optional: Disable specific core plugins
|
|
1445
|
-
corePlugins: {
|
|
1446
|
-
float: false,
|
|
1447
|
-
clear: false,
|
|
1448
|
-
},
|
|
1449
|
-
|
|
1450
|
-
// NEW: Styled components naming configuration
|
|
1451
|
-
styled: {
|
|
1452
|
-
prefix: 'twsx', // Global prefix (default: 'twsx')
|
|
1453
|
-
separator: '-', // Separator between parts (default: '-')
|
|
1454
|
-
hashLength: 6, // Hash length 4-8 (default: 6)
|
|
1455
|
-
includeComponentName: true // Include component type (default: true)
|
|
1456
|
-
}
|
|
1457
|
-
});
|
|
1458
|
-
```
|
|
1459
|
-
|
|
1460
|
-
**Examples:**
|
|
1461
|
-
|
|
1462
|
-
```javascript
|
|
1463
|
-
// Brand-specific prefix
|
|
1464
|
-
configure({ styled: { prefix: 'myapp' }})
|
|
1465
|
-
// Generated: myapp-button-a3k9x2
|
|
1466
|
-
|
|
1467
|
-
// Minimal classnames
|
|
1468
|
-
configure({
|
|
1469
|
-
styled: {
|
|
1470
|
-
prefix: 'c',
|
|
1471
|
-
separator: '',
|
|
1472
|
-
hashLength: 4,
|
|
1473
|
-
includeComponentName: false
|
|
1474
|
-
}
|
|
1475
|
-
})
|
|
1476
|
-
// Generated: c1a2b
|
|
1477
|
-
|
|
1478
|
-
// BEM-like style
|
|
1479
|
-
configure({ styled: { separator: '__' }})
|
|
1480
|
-
// Generated: twsx__button__a3k9x2
|
|
1481
|
-
```
|
|
1482
|
-
|
|
1483
|
-
See [Custom Classname Prefix Configuration](#custom-classname-prefix-configuration) for more details.
|
|
1484
|
-
|
|
1485
|
-
## Custom Plugins (v2.10.0+)
|
|
1486
|
-
|
|
1487
|
-
Create custom utility classes with the plugin API:
|
|
1488
|
-
|
|
1489
|
-
### Simple Utility Plugin
|
|
1490
|
-
|
|
1491
|
-
```javascript
|
|
1492
|
-
import { createPlugin, configure, tws } from "tailwind-to-style";
|
|
1493
|
-
|
|
1494
|
-
// Create a text gradient plugin
|
|
1495
|
-
const textGradientPlugin = createPlugin('text-gradient', {
|
|
1496
|
-
utilities: {
|
|
1497
|
-
'.text-gradient': {
|
|
1498
|
-
'background-clip': 'text',
|
|
1499
|
-
'-webkit-background-clip': 'text',
|
|
1500
|
-
'-webkit-text-fill-color': 'transparent',
|
|
1501
|
-
'background-image': 'linear-gradient(to right, #3b82f6, #8b5cf6)',
|
|
1502
|
-
},
|
|
1503
|
-
},
|
|
1504
|
-
});
|
|
1505
|
-
|
|
1506
|
-
// Register the plugin
|
|
1507
|
-
configure({
|
|
1508
|
-
plugins: [textGradientPlugin],
|
|
1509
|
-
});
|
|
1510
|
-
|
|
1511
|
-
// Use it!
|
|
1512
|
-
const heading = tws('text-gradient text-4xl font-bold', 1);
|
|
1513
|
-
```
|
|
1514
|
-
|
|
1515
|
-
### Dynamic Utility Plugin
|
|
1516
|
-
|
|
1517
|
-
```javascript
|
|
1518
|
-
import { createUtilityPlugin, configure } from "tailwind-to-style";
|
|
1519
|
-
|
|
1520
|
-
// Create a text-shadow plugin with multiple values
|
|
1521
|
-
const textShadowPlugin = createUtilityPlugin('text-shadow', {
|
|
1522
|
-
prefix: 'text-shadow',
|
|
1523
|
-
values: {
|
|
1524
|
-
sm: '1px 1px 2px rgba(0,0,0,0.1)',
|
|
1525
|
-
md: '2px 2px 4px rgba(0,0,0,0.15)',
|
|
1526
|
-
lg: '4px 4px 8px rgba(0,0,0,0.2)',
|
|
1527
|
-
xl: '6px 6px 12px rgba(0,0,0,0.25)',
|
|
1528
|
-
},
|
|
1529
|
-
formatter: (value) => ({
|
|
1530
|
-
'text-shadow': value,
|
|
1531
|
-
}),
|
|
1532
|
-
});
|
|
1533
|
-
|
|
1534
|
-
configure({
|
|
1535
|
-
plugins: [textShadowPlugin],
|
|
1536
|
-
});
|
|
1537
|
-
|
|
1538
|
-
// Use with different sizes
|
|
1539
|
-
tws('text-shadow-sm'); // Small shadow
|
|
1540
|
-
tws('text-shadow-lg'); // Large shadow
|
|
1541
|
-
```
|
|
1542
|
-
|
|
1543
|
-
### Glassmorphism Plugin Example
|
|
1544
|
-
|
|
1545
|
-
```javascript
|
|
1546
|
-
const glassmorphismPlugin = createPlugin('glassmorphism', {
|
|
1547
|
-
utilities: {
|
|
1548
|
-
'.glass': {
|
|
1549
|
-
'backdrop-filter': 'blur(10px)',
|
|
1550
|
-
'-webkit-backdrop-filter': 'blur(10px)',
|
|
1551
|
-
'background-color': 'rgba(255, 255, 255, 0.1)',
|
|
1552
|
-
'border': '1px solid rgba(255, 255, 255, 0.2)',
|
|
1553
|
-
},
|
|
1554
|
-
'.glass-dark': {
|
|
1555
|
-
'backdrop-filter': 'blur(10px)',
|
|
1556
|
-
'-webkit-backdrop-filter': 'blur(10px)',
|
|
1557
|
-
'background-color': 'rgba(0, 0, 0, 0.1)',
|
|
1558
|
-
'border': '1px solid rgba(255, 255, 255, 0.1)',
|
|
1559
|
-
},
|
|
1560
|
-
},
|
|
1561
|
-
});
|
|
1562
|
-
|
|
1563
|
-
configure({
|
|
1564
|
-
plugins: [glassmorphismPlugin],
|
|
1565
|
-
});
|
|
1566
|
-
|
|
1567
|
-
// Use glassmorphism effects
|
|
1568
|
-
const card = tws('glass p-6 rounded-lg', 1);
|
|
1569
|
-
```
|
|
1570
|
-
|
|
1571
|
-
### Complete Configuration Example
|
|
1572
|
-
|
|
1573
|
-
```javascript
|
|
1574
|
-
import { configure, createPlugin, createUtilityPlugin } from "tailwind-to-style";
|
|
1575
|
-
|
|
1576
|
-
const textShadowPlugin = createUtilityPlugin('text-shadow', {
|
|
1577
|
-
prefix: 'text-shadow',
|
|
1578
|
-
values: {
|
|
1579
|
-
sm: '1px 1px 2px rgba(0,0,0,0.1)',
|
|
1580
|
-
md: '2px 2px 4px rgba(0,0,0,0.15)',
|
|
1581
|
-
lg: '4px 4px 8px rgba(0,0,0,0.2)',
|
|
1582
|
-
},
|
|
1583
|
-
formatter: (value) => ({ 'text-shadow': value }),
|
|
1584
|
-
});
|
|
1585
|
-
|
|
1586
|
-
const glassmorphismPlugin = createPlugin('glassmorphism', {
|
|
1587
|
-
utilities: {
|
|
1588
|
-
'.glass': {
|
|
1589
|
-
'backdrop-filter': 'blur(10px)',
|
|
1590
|
-
'background-color': 'rgba(255, 255, 255, 0.1)',
|
|
1591
|
-
},
|
|
1592
|
-
},
|
|
1593
|
-
});
|
|
1594
|
-
|
|
1595
|
-
configure({
|
|
1596
|
-
theme: {
|
|
1597
|
-
extend: {
|
|
1598
|
-
colors: {
|
|
1599
|
-
brand: {
|
|
1600
|
-
500: '#0ea5e9',
|
|
1601
|
-
},
|
|
1602
|
-
},
|
|
1603
|
-
spacing: {
|
|
1604
|
-
'128': '32rem',
|
|
1605
|
-
},
|
|
1606
|
-
},
|
|
1607
|
-
},
|
|
1608
|
-
plugins: [textShadowPlugin, glassmorphismPlugin],
|
|
1609
|
-
});
|
|
1610
|
-
```
|
|
1611
|
-
|
|
1612
|
-
### 2. `twsx`
|
|
1613
|
-
|
|
1614
|
-
`twsx` is an advanced function that builds on `tws` by allowing you to define **nested styles** and more complex CSS structures. It supports **grouping**, **responsive variants**, **state variants**, **dynamic utilities**, and **direct CSS properties** via the `@css` directive, making it ideal for more advanced styling needs.
|
|
1615
|
-
|
|
1616
|
-
#### Features of `twsx`:
|
|
1617
|
-
|
|
1618
|
-
- â
**Nested styles** similar to SCSS, enabling more complex CSS structures
|
|
1619
|
-
- â
**Grouping**: Supports grouping utilities inside parentheses `hover:(bg-blue-600 scale-105)`
|
|
1620
|
-
- â
**Responsive variants** (`sm`, `md`, `lg`, `xl`, `2xl`) in standard and grouping syntax
|
|
1621
|
-
- â
**ð Responsive selector syntax** (v2.9.0+): `'md:.title': 'text-lg'` format for intuitive responsive styling
|
|
1622
|
-
- â
**State variants** like `hover`, `focus`, `active`, `disabled`, etc.
|
|
1623
|
-
- â
**Dynamic utilities** such as `w-[300px]`, `bg-[rgba(0,0,0,0.5)]`, `text-[14px]`
|
|
1624
|
-
- â
**!important support** with `!text-red-500`, `!bg-blue-500`
|
|
1625
|
-
- â
**ð Enhanced @css directive** (v2.9.0+): Perfect CSS variables, functions, and complex expressions support
|
|
1626
|
-
|
|
1627
|
-
#### Basic Usage
|
|
1628
|
-
|
|
1629
|
-
```javascript
|
|
1630
|
-
import { twsx } from "tailwind-to-style";
|
|
1631
|
-
|
|
1632
|
-
const styles = twsx({
|
|
1633
|
-
".card": [
|
|
1634
|
-
"bg-white p-4 rounded-lg shadow-md",
|
|
1635
|
-
{
|
|
1636
|
-
"&:hover": "shadow-xl scale-105",
|
|
1637
|
-
".title": "text-lg font-bold text-gray-900",
|
|
1638
|
-
".desc": "text-sm text-gray-600 mt-2",
|
|
1639
|
-
},
|
|
1640
|
-
],
|
|
1641
|
-
});
|
|
1642
|
-
|
|
1643
|
-
console.log(styles);
|
|
1644
|
-
```
|
|
1645
|
-
|
|
1646
|
-
**Output**:
|
|
1647
|
-
|
|
1648
|
-
```css
|
|
1649
|
-
.card {
|
|
1650
|
-
background-color: rgba(255, 255, 255, var(--bg-opacity));
|
|
1651
|
-
padding: 1rem;
|
|
1652
|
-
border-radius: 0.5rem;
|
|
1653
|
-
box-shadow:
|
|
1654
|
-
0 4px 6px -1px rgb(0 0 0 / 0.1),
|
|
1655
|
-
0 2px 4px -2px rgb(0 0 0 / 0.1);
|
|
1656
|
-
}
|
|
1657
|
-
.card:hover {
|
|
1658
|
-
box-shadow:
|
|
1659
|
-
0 20px 25px -5px rgb(0 0 0 / 0.1),
|
|
1660
|
-
0 8px 10px -6px rgb(0 0 0 / 0.1);
|
|
1661
|
-
transform: scale(1.05);
|
|
1662
|
-
}
|
|
1663
|
-
.card .title {
|
|
1664
|
-
font-size: 1.125rem;
|
|
1665
|
-
font-weight: 700;
|
|
1666
|
-
color: rgba(17, 24, 39, var(--text-opacity));
|
|
1667
|
-
}
|
|
1668
|
-
.card .desc {
|
|
1669
|
-
font-size: 0.875rem;
|
|
1670
|
-
color: rgba(75, 85, 99, var(--text-opacity));
|
|
1671
|
-
margin-top: 0.5rem;
|
|
1672
|
-
}
|
|
1673
|
-
```
|
|
1674
|
-
|
|
1675
|
-
#### Grouping Support
|
|
1676
|
-
|
|
1677
|
-
Group related utilities together inside parentheses for better readability and organization:
|
|
1678
|
-
|
|
1679
|
-
```javascript
|
|
1680
|
-
const styles = twsx({
|
|
1681
|
-
".button": [
|
|
1682
|
-
"bg-blue-500 text-white px-6 py-3 rounded-lg",
|
|
1683
|
-
"hover:(bg-blue-600 scale-105 shadow-lg)",
|
|
1684
|
-
"focus:(ring-2 ring-blue-300 outline-none)",
|
|
1685
|
-
"active:(bg-blue-700 scale-95)",
|
|
1686
|
-
],
|
|
1687
|
-
});
|
|
1688
|
-
```
|
|
1689
|
-
|
|
1690
|
-
**Output**:
|
|
1691
|
-
|
|
1692
|
-
```css
|
|
1693
|
-
.button {
|
|
1694
|
-
background-color: rgba(59, 130, 246, var(--bg-opacity));
|
|
1695
|
-
color: rgba(255, 255, 255, var(--text-opacity));
|
|
1696
|
-
padding: 0.75rem 1.5rem;
|
|
1697
|
-
border-radius: 0.5rem;
|
|
1698
|
-
}
|
|
1699
|
-
.button:hover {
|
|
1700
|
-
background-color: rgba(37, 99, 235, var(--bg-opacity));
|
|
1701
|
-
transform: scale(1.05);
|
|
1702
|
-
box-shadow:
|
|
1703
|
-
0 10px 15px -3px rgb(0 0 0 / 0.1),
|
|
1704
|
-
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
1705
|
-
}
|
|
1706
|
-
.button:focus {
|
|
1707
|
-
box-shadow: var(--ring-offset-shadow), var(--ring-shadow);
|
|
1708
|
-
outline: none;
|
|
1709
|
-
}
|
|
1710
|
-
.button:active {
|
|
1711
|
-
background-color: rgba(29, 78, 216, var(--bg-opacity));
|
|
1712
|
-
transform: scale(0.95);
|
|
1713
|
-
}
|
|
1714
|
-
```
|
|
1715
|
-
|
|
1716
|
-
#### Responsive Variants
|
|
1717
|
-
|
|
1718
|
-
Responsive variants work seamlessly with both standard syntax and grouping syntax:
|
|
1719
|
-
|
|
1720
|
-
```javascript
|
|
1721
|
-
const styles = twsx({
|
|
1722
|
-
".hero": [
|
|
1723
|
-
// Standard responsive syntax
|
|
1724
|
-
"text-2xl sm:text-3xl md:text-4xl lg:text-5xl xl:text-6xl",
|
|
1725
|
-
"w-full md:w-1/2 lg:w-1/3 p-4",
|
|
1726
|
-
// Grouped responsive syntax
|
|
1727
|
-
"sm:(py-16 px-4)",
|
|
1728
|
-
"md:(py-20 px-6)",
|
|
1729
|
-
"lg:(py-24 px-8)",
|
|
1730
|
-
{
|
|
1731
|
-
h1: "font-bold text-gray-900",
|
|
1732
|
-
p: "text-gray-600 mt-4",
|
|
1733
|
-
},
|
|
1734
|
-
],
|
|
1735
|
-
});
|
|
1736
|
-
```
|
|
1737
|
-
|
|
1738
|
-
**Output**:
|
|
1739
|
-
|
|
1740
|
-
```css
|
|
1741
|
-
.hero {
|
|
1742
|
-
font-size: 1.5rem;
|
|
1743
|
-
width: 100%;
|
|
1744
|
-
padding: 1rem;
|
|
1745
|
-
}
|
|
1746
|
-
@media (min-width: 640px) {
|
|
1747
|
-
.hero {
|
|
1748
|
-
font-size: 1.875rem;
|
|
1749
|
-
padding: 4rem 1rem;
|
|
1750
|
-
}
|
|
1751
|
-
}
|
|
1752
|
-
@media (min-width: 768px) {
|
|
1753
|
-
.hero {
|
|
1754
|
-
font-size: 2.25rem;
|
|
1755
|
-
width: 50%;
|
|
1756
|
-
padding: 5rem 1.5rem;
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
@media (min-width: 1024px) {
|
|
1760
|
-
.hero {
|
|
1761
|
-
font-size: 3rem;
|
|
1762
|
-
width: 33.333333%;
|
|
1763
|
-
padding: 6rem 2rem;
|
|
1764
|
-
}
|
|
1765
|
-
}
|
|
1766
|
-
@media (min-width: 1280px) {
|
|
1767
|
-
.hero {
|
|
1768
|
-
font-size: 3.75rem;
|
|
1769
|
-
}
|
|
1770
|
-
}
|
|
1771
|
-
.hero h1 {
|
|
1772
|
-
font-weight: 700;
|
|
1773
|
-
color: rgba(17, 24, 39, var(--text-opacity));
|
|
1774
|
-
}
|
|
1775
|
-
.hero p {
|
|
1776
|
-
color: rgba(75, 85, 99, var(--text-opacity));
|
|
1777
|
-
margin-top: 1rem;
|
|
1778
|
-
}
|
|
1779
|
-
```
|
|
1780
|
-
|
|
1781
|
-
#### ð Responsive Selector Syntax (v2.9.0+)
|
|
1782
|
-
|
|
1783
|
-
**New feature**: You can now use responsive breakpoints directly in selectors for more intuitive responsive styling:
|
|
1784
|
-
|
|
1785
|
-
```javascript
|
|
1786
|
-
const styles = twsx({
|
|
1787
|
-
// New responsive selector syntax
|
|
1788
|
-
"md:.title": "text-lg font-bold",
|
|
1789
|
-
"lg:.title": "text-xl",
|
|
1790
|
-
"xl:.title": "text-2xl",
|
|
1791
|
-
|
|
1792
|
-
// Equivalent to the traditional syntax:
|
|
1793
|
-
".title": "md:text-lg md:font-bold lg:text-xl xl:text-2xl"
|
|
1794
|
-
});
|
|
1795
|
-
```
|
|
1796
|
-
|
|
1797
|
-
This new syntax automatically converts responsive selectors to traditional Tailwind responsive classes and generates proper media queries:
|
|
1798
|
-
|
|
1799
|
-
```css
|
|
1800
|
-
.title {
|
|
1801
|
-
/* Base styles if any */
|
|
1802
|
-
}
|
|
1803
|
-
@media (min-width: 768px) {
|
|
1804
|
-
.title {
|
|
1805
|
-
font-size: 1.125rem;
|
|
1806
|
-
font-weight: 700;
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1809
|
-
@media (min-width: 1024px) {
|
|
1810
|
-
.title {
|
|
1811
|
-
font-size: 1.25rem;
|
|
1812
|
-
}
|
|
1813
|
-
}
|
|
1814
|
-
@media (min-width: 1280px) {
|
|
1815
|
-
.title {
|
|
1816
|
-
font-size: 1.5rem;
|
|
1817
|
-
}
|
|
1818
|
-
}
|
|
1819
|
-
```
|
|
1820
|
-
|
|
1821
|
-
**Benefits of Responsive Selector Syntax:**
|
|
1822
|
-
- â
More intuitive and organized responsive code
|
|
1823
|
-
- â
Better separation of breakpoint-specific styles
|
|
1824
|
-
- â
Easier to maintain complex responsive designs
|
|
1825
|
-
- â
Backward compatible with existing syntax
|
|
1826
|
-
- â
Works with all breakpoints: `sm`, `md`, `lg`, `xl`, `2xl`
|
|
1827
|
-
|
|
1828
|
-
#### Handling Dark and Light Mode
|
|
1829
|
-
|
|
1830
|
-
`twsx` supports writing styles for dark and light mode easily, similar to Tailwind CSS. You can use the `dark:` prefix, `.dark` selector, grouping, or nested selector.
|
|
1831
|
-
|
|
1832
|
-
**Usage Example:**
|
|
1833
|
-
|
|
1834
|
-
```javascript
|
|
1835
|
-
import { twsx } from "tailwind-to-style";
|
|
1836
|
-
|
|
1837
|
-
const styles = twsx({
|
|
1838
|
-
".card": [
|
|
1839
|
-
"bg-white text-gray-900 dark:bg-gray-900 dark:text-white", // Using dark: prefix
|
|
1840
|
-
{ ".dark &": "bg-gray-900 text-white border-gray-700" }, // Or .dark selector
|
|
1841
|
-
],
|
|
1842
|
-
// Grouping is also supported
|
|
1843
|
-
".button": "bg-blue-500 text-white dark:(bg-gray-900 text-yellow-200)",
|
|
1844
|
-
});
|
|
1845
|
-
```
|
|
1846
|
-
|
|
1847
|
-
**Explanation:**
|
|
1848
|
-
- The `dark:` prefix will automatically generate CSS that is only active if the parent has the `dark` class (e.g. `<html class="dark">`).
|
|
1849
|
-
- The `.dark &` selector can also be used for more flexibility.
|
|
1850
|
-
- Grouping `dark:(...)` will be automatically expanded into multiple dark classes.
|
|
1851
|
-
|
|
1852
|
-
**CSS Output:**
|
|
1853
|
-
```css
|
|
1854
|
-
.card {
|
|
1855
|
-
background-color: #fff;
|
|
1856
|
-
color: #111827;
|
|
1857
|
-
}
|
|
1858
|
-
.dark .card {
|
|
1859
|
-
background-color: #111827;
|
|
1860
|
-
color: #fff;
|
|
1861
|
-
border-color: #374151;
|
|
1862
|
-
}
|
|
1863
|
-
.button {
|
|
1864
|
-
background-color: #3b82f6;
|
|
1865
|
-
color: #fff;
|
|
1866
|
-
}
|
|
1867
|
-
.dark .button {
|
|
1868
|
-
background-color: #111827;
|
|
1869
|
-
color: #fde68a;
|
|
1870
|
-
}
|
|
1871
|
-
```
|
|
1872
|
-
|
|
1873
|
-
**Enable dark mode** by adding the `dark` class to the root element (usually `<html class="dark">`).
|
|
1874
|
-
|
|
1875
|
-
### Performance Utilities
|
|
1876
|
-
|
|
1877
|
-
The library includes performance optimization features:
|
|
1878
|
-
|
|
1879
|
-
#### Inject Option
|
|
1880
|
-
|
|
1881
|
-
Control CSS output location with the `inject` option:
|
|
1882
|
-
|
|
1883
|
-
```javascript
|
|
1884
|
-
import { tws, twsx } from "tailwind-to-style";
|
|
1885
|
-
|
|
1886
|
-
// Auto-inject into document head (default)
|
|
1887
|
-
const styles1 = tws("bg-blue-500 text-white p-4");
|
|
1888
|
-
|
|
1889
|
-
// Skip injection - returns CSS only
|
|
1890
|
-
const styles2 = tws("bg-red-500 text-black p-2", { inject: false });
|
|
1891
|
-
|
|
1892
|
-
// Custom injection target
|
|
1893
|
-
const targetElement = document.getElementById("custom-styles");
|
|
1894
|
-
const styles3 = tws("bg-green-500 text-yellow p-3", { inject: targetElement });
|
|
1895
|
-
```
|
|
1896
|
-
|
|
1897
|
-
#### Performance Monitoring
|
|
1898
|
-
|
|
1899
|
-
```javascript
|
|
1900
|
-
import { tws } from "tailwind-to-style";
|
|
1901
|
-
|
|
1902
|
-
// Enable performance logging
|
|
1903
|
-
const start = performance.now();
|
|
1904
|
-
const styles = tws("complex-classes here...");
|
|
1905
|
-
const end = performance.now();
|
|
1906
|
-
console.log(`Generation time: ${end - start}ms`);
|
|
1907
|
-
```
|
|
1908
|
-
|
|
1909
|
-
## Advanced `@css` Directive
|
|
1910
|
-
|
|
1911
|
-
The `@css` directive allows you to write custom CSS properties that aren't available as Tailwind utilities. **Starting from v2.9.0**, the `@css` directive has been significantly enhanced with improved CSS syntax preservation.
|
|
1912
|
-
|
|
1913
|
-
### ð Enhanced CSS Support (v2.9.0+)
|
|
1914
|
-
|
|
1915
|
-
**Major improvements in CSS handling:**
|
|
1916
|
-
- â
**Perfect CSS Variables**: `var(--custom-property)` syntax fully preserved
|
|
1917
|
-
- â
**CSS Functions**: `calc()`, `rgba()`, `linear-gradient()`, `clamp()` etc. work flawlessly
|
|
1918
|
-
- â
**Complex Expressions**: Multi-function CSS expressions preserved accurately
|
|
1919
|
-
- â
**Zero Corruption**: Fixed critical bug where CSS values were being corrupted
|
|
1920
|
-
|
|
1921
|
-
**Before v2.9.0** (corrupted):
|
|
1922
|
-
```css
|
|
1923
|
-
/* This would be corrupted */
|
|
1924
|
-
background: -var--primary; /* â WRONG */
|
|
1925
|
-
color: rgba-255,0,0,0.5; /* â WRONG */
|
|
1926
|
-
```
|
|
1927
|
-
|
|
1928
|
-
**v2.9.0+** (perfect preservation):
|
|
1929
|
-
```css
|
|
1930
|
-
/* Now works perfectly */
|
|
1931
|
-
background: var(--primary); /* â
CORRECT */
|
|
1932
|
-
color: rgba(255,0,0,0.5); /* â
CORRECT */
|
|
1933
|
-
transform: calc(100% - 20px); /* â
CORRECT */
|
|
1934
|
-
```
|
|
1935
|
-
|
|
1936
|
-
### Usage Examples
|
|
1937
|
-
|
|
1938
|
-
There are several ways to use the `@css` feature:
|
|
1939
|
-
|
|
1940
|
-
1. **As a nested object inside selectors**:
|
|
1941
|
-
|
|
1942
|
-
```javascript
|
|
1943
|
-
const styles = twsx({
|
|
1944
|
-
".button": [
|
|
1945
|
-
"bg-blue-500 text-white rounded-md",
|
|
1946
|
-
{
|
|
1947
|
-
"@css": {
|
|
1948
|
-
transition: "all 0.3s ease-in-out",
|
|
1949
|
-
"will-change": "transform, opacity",
|
|
1950
|
-
},
|
|
1951
|
-
"&:hover": "bg-blue-600",
|
|
1952
|
-
},
|
|
1953
|
-
],
|
|
1954
|
-
});
|
|
1955
|
-
```
|
|
1956
|
-
|
|
1957
|
-
**Output**:
|
|
1958
|
-
|
|
1959
|
-
```css
|
|
1960
|
-
.button {
|
|
1961
|
-
background-color: #3b82f6;
|
|
1962
|
-
color: white;
|
|
1963
|
-
border-radius: 0.375rem;
|
|
1964
|
-
transition: all 0.3s ease-in-out;
|
|
1965
|
-
will-change: transform, opacity;
|
|
1966
|
-
}
|
|
1967
|
-
.button:hover {
|
|
1968
|
-
background-color: #2563eb;
|
|
1969
|
-
}
|
|
1970
|
-
```
|
|
1971
|
-
|
|
1972
|
-
2. **As a direct property in the selector**:
|
|
1973
|
-
|
|
1974
|
-
```javascript
|
|
1975
|
-
const styles = twsx({
|
|
1976
|
-
".button @css transition": "all 0.3s ease-in-out",
|
|
1977
|
-
".button": "bg-blue-500 text-white rounded-md",
|
|
1978
|
-
".button:hover": "bg-blue-600",
|
|
1979
|
-
});
|
|
1980
|
-
```
|
|
1981
|
-
|
|
1982
|
-
This syntax is especially useful when you need to add just a single CSS property that isn't available in Tailwind.
|
|
1983
|
-
|
|
1984
|
-
The `@css` feature is particularly helpful for properties that require complex values with spaces (like transitions, animations, and transforms) which can't be represented with standard Tailwind utility classes.
|
|
1985
|
-
|
|
1986
|
-
#### Advanced `@css` Examples:
|
|
1987
|
-
|
|
1988
|
-
You can combine `@css` with state variants:
|
|
1989
|
-
|
|
1990
|
-
```javascript
|
|
1991
|
-
const styles = twsx({
|
|
1992
|
-
".modal": [
|
|
1993
|
-
"bg-white rounded-lg shadow-xl",
|
|
1994
|
-
{
|
|
1995
|
-
"@css": {
|
|
1996
|
-
transform: "translateX(0px)",
|
|
1997
|
-
transition: "all 0.3s ease-out",
|
|
1998
|
-
"will-change": "transform, opacity",
|
|
1999
|
-
},
|
|
2000
|
-
"&.hidden": [
|
|
2001
|
-
"opacity-0",
|
|
2002
|
-
{
|
|
2003
|
-
"@css": {
|
|
2004
|
-
transform: "translateX(-100px)",
|
|
2005
|
-
},
|
|
2006
|
-
},
|
|
2007
|
-
],
|
|
2008
|
-
},
|
|
2009
|
-
],
|
|
2010
|
-
});
|
|
2011
|
-
```
|
|
2012
|
-
|
|
2013
|
-
**Output**:
|
|
2014
|
-
|
|
2015
|
-
```css
|
|
2016
|
-
.modal {
|
|
2017
|
-
background-color: white;
|
|
2018
|
-
border-radius: 0.5rem;
|
|
2019
|
-
box-shadow:
|
|
2020
|
-
0 20px 25px -5px rgba(0, 0, 0, 0.1),
|
|
2021
|
-
0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
|
2022
|
-
transform: translateX(0px);
|
|
2023
|
-
transition: all 0.3s ease-out;
|
|
2024
|
-
will-change: transform, opacity;
|
|
2025
|
-
}
|
|
2026
|
-
.modal.hidden {
|
|
2027
|
-
opacity: 0;
|
|
2028
|
-
transform: translateX(-100px);
|
|
2029
|
-
}
|
|
2030
|
-
```
|
|
2031
|
-
|
|
2032
|
-
#### ð CSS Variables & Functions Examples (v2.9.0+)
|
|
2033
|
-
|
|
2034
|
-
With the enhanced `@css` directive, you can now use complex CSS features:
|
|
2035
|
-
|
|
2036
|
-
```javascript
|
|
2037
|
-
const styles = twsx({
|
|
2038
|
-
".theme-component": {
|
|
2039
|
-
"@css": {
|
|
2040
|
-
// CSS Variables - now work perfectly!
|
|
2041
|
-
"--primary-color": "#3b82f6",
|
|
2042
|
-
"--secondary-color": "#8b5cf6",
|
|
2043
|
-
"--border-radius": "0.5rem",
|
|
2044
|
-
|
|
2045
|
-
// CSS Functions - fully preserved!
|
|
2046
|
-
"background": "linear-gradient(135deg, var(--primary-color), var(--secondary-color))",
|
|
2047
|
-
"border-radius": "var(--border-radius)",
|
|
2048
|
-
"box-shadow": "0 4px 20px rgba(0, 0, 0, 0.15)",
|
|
2049
|
-
"transform": "translateY(calc(-1 * var(--spacing, 10px)))",
|
|
2050
|
-
|
|
2051
|
-
// Complex CSS expressions
|
|
2052
|
-
"width": "clamp(200px, 50vw, 800px)",
|
|
2053
|
-
"padding": "calc(1rem + 2vw)",
|
|
2054
|
-
"color": "hsl(220, 100%, 50%)"
|
|
2055
|
-
}
|
|
2056
|
-
},
|
|
2057
|
-
|
|
2058
|
-
".dynamic-grid": {
|
|
2059
|
-
"@css": {
|
|
2060
|
-
"display": "grid",
|
|
2061
|
-
"grid-template-columns": "repeat(auto-fit, minmax(250px, 1fr))",
|
|
2062
|
-
"gap": "clamp(1rem, 5vw, 3rem)",
|
|
2063
|
-
"grid-auto-rows": "minmax(200px, auto)"
|
|
2064
|
-
}
|
|
2065
|
-
}
|
|
2066
|
-
});
|
|
2067
|
-
```
|
|
2068
|
-
|
|
2069
|
-
**Output** (perfectly preserved CSS):
|
|
2070
|
-
|
|
2071
|
-
```css
|
|
2072
|
-
.theme-component {
|
|
2073
|
-
--primary-color: #3b82f6;
|
|
2074
|
-
--secondary-color: #8b5cf6;
|
|
2075
|
-
--border-radius: 0.5rem;
|
|
2076
|
-
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
|
|
2077
|
-
border-radius: var(--border-radius);
|
|
2078
|
-
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|
2079
|
-
transform: translateY(calc(-1 * var(--spacing, 10px)));
|
|
2080
|
-
width: clamp(200px, 50vw, 800px);
|
|
2081
|
-
padding: calc(1rem + 2vw);
|
|
2082
|
-
color: hsl(220, 100%, 50%);
|
|
2083
|
-
}
|
|
2084
|
-
.dynamic-grid {
|
|
2085
|
-
display: grid;
|
|
2086
|
-
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
2087
|
-
gap: clamp(1rem, 5vw, 3rem);
|
|
2088
|
-
grid-auto-rows: minmax(200px, auto);
|
|
2089
|
-
}
|
|
2090
|
-
```
|
|
2091
|
-
|
|
2092
|
-
For responsive styles, you can use standard Tailwind responsive utilities within your classes:
|
|
2093
|
-
|
|
2094
|
-
```javascript
|
|
2095
|
-
const styles = twsx({
|
|
2096
|
-
".responsive-box": "w-full md:w-1/2 lg:w-1/3 p-4 bg-blue-500",
|
|
2097
|
-
});
|
|
2098
|
-
```
|
|
2099
|
-
|
|
2100
|
-
The `@css` feature provides a powerful way to bridge the gap between Tailwind's utility classes and custom CSS when needed, without leaving the `twsx` syntax.
|
|
2101
|
-
|
|
2102
|
-
### Inject Option (Browser Only)
|
|
2103
|
-
|
|
2104
|
-
By default, every call to `twsx` in the browser will automatically inject the generated CSS into a `<style id="twsx-auto-style">` tag in the document `<head>`. This makes it easy to use dynamic styles in browser or CDN scenarios without manual CSS management.
|
|
2105
|
-
|
|
2106
|
-
You can control this behavior with the `inject` option:
|
|
2107
|
-
|
|
2108
|
-
```js
|
|
2109
|
-
// Auto-inject (default)
|
|
2110
|
-
twsx({ ... }) // CSS is injected automatically
|
|
2111
|
-
|
|
2112
|
-
// Disable auto-inject
|
|
2113
|
-
twsx({ ... }, { inject: false }) // CSS is NOT injected, just returned as string
|
|
2114
|
-
```
|
|
2115
|
-
|
|
2116
|
-
- **inject: true** (default): CSS is injected into the page (browser only).
|
|
2117
|
-
- **inject: false**: CSS is only returned as a string, not injected. Useful for SSR, testing, or custom handling.
|
|
2118
|
-
|
|
2119
|
-
> Note: This option only affects browser usage. In Node.js or SSR, no injection occurs.
|
|
2120
|
-
|
|
2121
|
-
## Performance Monitoring & Debugging (v2.7.0+)
|
|
2122
|
-
|
|
2123
|
-
Starting from version 2.7.0, `tailwind-to-style` includes built-in performance monitoring and debugging utilities to help you optimize your application and troubleshoot issues.
|
|
2124
|
-
|
|
2125
|
-
### Performance Utils
|
|
2126
|
-
|
|
2127
|
-
```javascript
|
|
2128
|
-
import { performanceUtils } from "tailwind-to-style";
|
|
2129
|
-
|
|
2130
|
-
// Enable performance logging (logs operations > 5ms as warnings)
|
|
2131
|
-
performanceUtils.enablePerformanceLogging(true);
|
|
2132
|
-
|
|
2133
|
-
// Get cache and injection statistics
|
|
2134
|
-
const stats = performanceUtils.getStats();
|
|
2135
|
-
console.log(stats);
|
|
2136
|
-
// Output:
|
|
2137
|
-
// {
|
|
2138
|
-
// cacheStats: {
|
|
2139
|
-
// cssResolution: 45,
|
|
2140
|
-
// configOptions: 2,
|
|
2141
|
-
// parseSelector: 23,
|
|
2142
|
-
// encodeBracket: 12,
|
|
2143
|
-
// decodeBracket: 8
|
|
2144
|
-
// },
|
|
2145
|
-
// injectionStats: {
|
|
2146
|
-
// uniqueStylesheets: 15
|
|
2147
|
-
// }
|
|
2148
|
-
// }
|
|
2149
|
-
|
|
2150
|
-
// Clear all caches (useful for memory management)
|
|
2151
|
-
performanceUtils.clearCaches();
|
|
2152
|
-
```
|
|
2153
|
-
|
|
2154
|
-
### Performance Metrics
|
|
2155
|
-
|
|
2156
|
-
The library automatically tracks performance for key operations:
|
|
2157
|
-
|
|
2158
|
-
- **tws:total** - Total execution time for `tws()`
|
|
2159
|
-
- **tws:parse** - Time spent parsing classes
|
|
2160
|
-
- **tws:process** - Time spent processing classes
|
|
2161
|
-
- **twsx:total** - Total execution time for `twsx()`
|
|
2162
|
-
- **twsx:flatten** - Time spent flattening objects
|
|
2163
|
-
- **twsx:generate** - Time spent generating CSS
|
|
2164
|
-
- **css:inject** - Time spent injecting CSS to DOM
|
|
2165
|
-
|
|
2166
|
-
### Debounced Functions
|
|
2167
|
-
|
|
2168
|
-
For high-frequency usage, use the debounced versions:
|
|
2169
|
-
|
|
2170
|
-
```javascript
|
|
2171
|
-
import { debouncedTws, debouncedTwsx } from "tailwind-to-style";
|
|
2172
|
-
|
|
2173
|
-
// Debounced versions (50ms for tws, 100ms for twsx)
|
|
2174
|
-
const styles = debouncedTws("bg-red-500 p-4");
|
|
2175
|
-
const complexStyles = debouncedTwsx({ ".card": "bg-white p-6" });
|
|
2176
|
-
```
|
|
2177
|
-
|
|
2178
|
-
### Example: Performance Monitoring
|
|
2179
|
-
|
|
2180
|
-
```javascript
|
|
2181
|
-
import { tws, twsx, performanceUtils } from "tailwind-to-style";
|
|
2182
|
-
|
|
2183
|
-
// Enable monitoring
|
|
2184
|
-
performanceUtils.enablePerformanceLogging(true);
|
|
2185
|
-
|
|
2186
|
-
// Your code that uses tws/twsx
|
|
2187
|
-
const styles = tws("bg-gradient-to-r from-purple-400 to-pink-500 p-8");
|
|
2188
|
-
const complexStyles = twsx({
|
|
2189
|
-
".hero": [
|
|
2190
|
-
"bg-gradient-to-br from-indigo-900 to-purple-900 min-h-screen",
|
|
2191
|
-
{
|
|
2192
|
-
h1: "text-6xl font-bold text-white md:text-4xl",
|
|
2193
|
-
"@css": {
|
|
2194
|
-
transition: "font-size 0.3s ease-in-out",
|
|
2195
|
-
},
|
|
2196
|
-
},
|
|
2197
|
-
],
|
|
2198
|
-
});
|
|
2199
|
-
|
|
2200
|
-
// Check performance stats
|
|
2201
|
-
console.log(performanceUtils.getStats());
|
|
2202
|
-
```
|
|
2203
|
-
|
|
2204
|
-
This will automatically log warnings for operations taking longer than 5ms and provide insights into cache usage and performance bottlenecks.
|
|
2205
|
-
|
|
2206
|
-
# Build-Time Plugins: Vite & Webpack
|
|
2207
|
-
|
|
2208
|
-
### Automated Modular CSS Generation
|
|
2209
|
-
|
|
2210
|
-
1. Create JS files with `twsx.` prefix in your project (e.g., `twsx.card.js`, `twsx.button.js`) anywhere in your `src/` folder.
|
|
2211
|
-
2. Use the Vite/Webpack plugin from the `plugins/` folder to automatically generate CSS on every build/rebuild.
|
|
2212
|
-
3. Each JS file will generate its own CSS file in the specified output directory (default: `src/styles/`).
|
|
2213
|
-
4. Import the generated CSS files directly in your components or bundle them as needed.
|
|
2214
|
-
|
|
2215
|
-
#### Vite Plugin Usage Example
|
|
2216
|
-
|
|
2217
|
-
Add the plugin to your `vite.config.js`:
|
|
2218
|
-
```js
|
|
2219
|
-
import twsxPlugin from 'tailwind-to-style/plugins/vite-twsx';
|
|
2220
|
-
|
|
2221
|
-
export default {
|
|
2222
|
-
plugins: [
|
|
2223
|
-
twsxPlugin({
|
|
2224
|
-
inputDir: 'src',
|
|
2225
|
-
outputDir: 'src/styles',
|
|
2226
|
-
preserveStructure: false // Set to true to generate CSS next to JS files
|
|
2227
|
-
})
|
|
2228
|
-
]
|
|
2229
|
-
};
|
|
2230
|
-
```
|
|
2231
|
-
|
|
2232
|
-
**Configuration Options:**
|
|
2233
|
-
- `inputDir`: Directory to scan for `twsx.*.js` files (default: `'src'`)
|
|
2234
|
-
- `outputDir`: Directory where CSS files will be generated (default: `'src/styles'`)
|
|
2235
|
-
- `preserveStructure`: Whether to generate CSS files next to their JS counterparts (default: `false`)
|
|
2236
|
-
|
|
2237
|
-
**Default mode:** CSS files created in `src/styles/` (e.g., `twsx.card.css`, `twsx.button.css`).
|
|
2238
|
-
**Preserve structure mode:** CSS files created next to JS files (e.g., `src/components/Card/twsx.card.css`).
|
|
2239
|
-
|
|
2240
|
-
Import in your components:
|
|
2241
|
-
```js
|
|
2242
|
-
// Default mode
|
|
2243
|
-
import './styles/twsx.card.css';
|
|
2244
|
-
import './styles/twsx.button.css';
|
|
2245
|
-
|
|
2246
|
-
// Preserve structure mode
|
|
2247
|
-
import './twsx.card.css'; // If in same directory
|
|
2248
|
-
```
|
|
2249
|
-
|
|
2250
|
-
#### Webpack Plugin Usage Example
|
|
2251
|
-
|
|
2252
|
-
Add the plugin to your `webpack.config.js`:
|
|
2253
|
-
```js
|
|
2254
|
-
import TwsxPlugin from 'tailwind-to-style/plugins/webpack-twsx';
|
|
2255
|
-
|
|
2256
|
-
module.exports = {
|
|
2257
|
-
plugins: [
|
|
2258
|
-
new TwsxPlugin({
|
|
2259
|
-
inputDir: 'src',
|
|
2260
|
-
outputDir: 'src/styles',
|
|
2261
|
-
preserveStructure: false // Set to true to generate CSS next to JS files
|
|
2262
|
-
})
|
|
2263
|
-
]
|
|
2264
|
-
};
|
|
2265
|
-
```
|
|
2266
|
-
|
|
2267
|
-
**Configuration Options:**
|
|
2268
|
-
- `inputDir`: Directory to scan for `twsx.*.js` files (default: `'src'`)
|
|
2269
|
-
- `outputDir`: Directory where CSS files will be generated (default: `'src/styles'`)
|
|
2270
|
-
- `preserveStructure`: Whether to generate CSS files next to their JS counterparts (default: `false`)
|
|
2271
|
-
|
|
2272
|
-
**Default mode:** CSS files created in `src/styles/` (e.g., `twsx.card.css`, `twsx.button.css`).
|
|
2273
|
-
**Preserve structure mode:** CSS files created next to JS files (e.g., `src/components/Card/twsx.card.css`).
|
|
2274
|
-
|
|
2275
|
-
Import in your components:
|
|
2276
|
-
```js
|
|
2277
|
-
// Default mode
|
|
2278
|
-
import './styles/twsx.card.css';
|
|
2279
|
-
import './styles/twsx.button.css';
|
|
2280
|
-
|
|
2281
|
-
// Preserve structure mode
|
|
2282
|
-
import './twsx.card.css'; // If in same directory
|
|
2283
|
-
```
|
|
2284
|
-
|
|
2285
|
-
## Build-Time CSS Generation via Script
|
|
2286
|
-
|
|
2287
|
-
In addition to using the Vite/Webpack plugin, you can also use a Node.js script to generate CSS files from `twsx.*.js` files manually or as part of your build workflow.
|
|
2288
|
-
|
|
2289
|
-
### Script: tailwind-to-style/lib/twsx-cli.js (Legacy)
|
|
2290
|
-
|
|
2291
|
-
> **ðĄ Recommended:** Use `npx twsx-cli` instead of calling the script directly.
|
|
2292
|
-
|
|
2293
|
-
This script will recursively scan for all `twsx.*.js` files in your project, generate CSS using the `twsx` function, and write individual CSS files to the specified output directory.
|
|
2294
|
-
|
|
2295
|
-
#### How to Use
|
|
2296
|
-
|
|
2297
|
-
1. Create JS files with `twsx.` prefix containing style objects anywhere in your `src/` folder (e.g., `src/components/twsx.card.js`).
|
|
2298
|
-
|
|
2299
|
-
2. **One-time Build:**
|
|
2300
|
-
```bash
|
|
2301
|
-
node tailwind-to-style/lib/twsx-cli.js
|
|
2302
|
-
```
|
|
2303
|
-
|
|
2304
|
-
3. **Watch Mode (Auto-rebuild on file changes):**
|
|
2305
|
-
```bash
|
|
2306
|
-
node tailwind-to-style/lib/twsx-cli.js --watch
|
|
2307
|
-
```
|
|
2308
|
-
|
|
2309
|
-
4. **Preserve Structure Mode (Generate CSS next to JS files):**
|
|
2310
|
-
```bash
|
|
2311
|
-
# One-time build with preserve structure
|
|
2312
|
-
node tailwind-to-style/lib/twsx-cli.js --preserve-structure
|
|
2313
|
-
|
|
2314
|
-
# Watch mode with preserve structure
|
|
2315
|
-
node tailwind-to-style/lib/twsx-cli.js --watch --preserve-structure
|
|
2316
|
-
```
|
|
2317
|
-
|
|
2318
|
-
You can configure input and output directories using environment variables:
|
|
2319
|
-
```bash
|
|
2320
|
-
TWSX_INPUT_DIR=src TWSX_OUTPUT_DIR=dist/styles node tailwind-to-style/lib/twsx-cli.js --watch
|
|
2321
|
-
```
|
|
2322
|
-
|
|
2323
|
-
Or use environment variables for preserve structure:
|
|
2324
|
-
```bash
|
|
2325
|
-
TWSX_PRESERVE_STRUCTURE=true node tailwind-to-style/lib/twsx-cli.js --watch
|
|
2326
|
-
```
|
|
2327
|
-
|
|
2328
|
-
5. **Output locations:**
|
|
2329
|
-
|
|
2330
|
-
**Default mode:** CSS files will be in the output directory (default: `src/styles/`):
|
|
2331
|
-
```
|
|
2332
|
-
src/styles/twsx.card.css
|
|
2333
|
-
src/styles/twsx.button.css
|
|
2334
|
-
```
|
|
2335
|
-
|
|
2336
|
-
**Preserve structure mode:** CSS files will be generated next to their JS counterparts:
|
|
2337
|
-
```
|
|
2338
|
-
src/components/Button/twsx.button.js â src/components/Button/twsx.button.css
|
|
2339
|
-
src/components/Card/twsx.card.js â src/components/Card/twsx.card.css
|
|
2340
|
-
```
|
|
2341
|
-
|
|
2342
|
-
5. Import the generated CSS files in your components:
|
|
2343
|
-
|
|
2344
|
-
**Default mode:**
|
|
2345
|
-
```js
|
|
2346
|
-
import './styles/twsx.card.css';
|
|
2347
|
-
import './styles/twsx.button.css';
|
|
2348
|
-
```
|
|
2349
|
-
|
|
2350
|
-
**Preserve structure mode:**
|
|
2351
|
-
```js
|
|
2352
|
-
// In src/components/Button/Button.jsx
|
|
2353
|
-
import './twsx.button.css';
|
|
2354
|
-
|
|
2355
|
-
// In src/components/Card/Card.jsx
|
|
2356
|
-
import './twsx.card.css';
|
|
2357
|
-
```
|
|
2358
|
-
|
|
2359
|
-
#### Usage in Different Projects
|
|
2360
|
-
|
|
2361
|
-
**React/Next.js/Vue/Any Project:**
|
|
2362
|
-
|
|
2363
|
-
1. Install the package:
|
|
2364
|
-
```bash
|
|
2365
|
-
npm install tailwind-to-style
|
|
2366
|
-
```
|
|
2367
|
-
|
|
2368
|
-
2. Add to your `package.json`:
|
|
2369
|
-
```json
|
|
2370
|
-
{
|
|
2371
|
-
"scripts": {
|
|
2372
|
-
"twsx:build": "node node_modules/tailwind-to-style/lib/twsx-cli.js",
|
|
2373
|
-
"twsx:watch": "node node_modules/tailwind-to-style/lib/twsx-cli.js --watch",
|
|
2374
|
-
"twsx:preserve": "node node_modules/tailwind-to-style/lib/twsx-cli.js --preserve-structure",
|
|
2375
|
-
"twsx:dev": "node node_modules/tailwind-to-style/lib/twsx-cli.js --watch --preserve-structure",
|
|
2376
|
-
"dev": "npm run twsx:watch & next dev"
|
|
2377
|
-
}
|
|
2378
|
-
}
|
|
2379
|
-
```
|
|
2380
|
-
|
|
2381
|
-
3. For development with auto-rebuild:
|
|
2382
|
-
```bash
|
|
2383
|
-
npm run twsx:watch
|
|
2384
|
-
```
|
|
2385
|
-
|
|
2386
|
-
4. For production build:
|
|
2387
|
-
```bash
|
|
2388
|
-
npm run twsx:build
|
|
2389
|
-
```
|
|
2390
|
-
|
|
2391
|
-
**VS Code Integration:**
|
|
2392
|
-
Add to your workspace settings (`.vscode/settings.json`):
|
|
2393
|
-
```json
|
|
2394
|
-
{
|
|
2395
|
-
"emeraldwalk.runonsave": {
|
|
2396
|
-
"commands": [
|
|
2397
|
-
{
|
|
2398
|
-
"match": "twsx\\..*\\.js$",
|
|
2399
|
-
"cmd": "npm run twsx:build"
|
|
2400
|
-
}
|
|
2401
|
-
]
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
```
|
|
2405
|
-
|
|
2406
|
-
#### Automatic Integration
|
|
2407
|
-
|
|
2408
|
-
You can add this script to the build section in your `package.json`:
|
|
2409
|
-
|
|
2410
|
-
```json
|
|
2411
|
-
{
|
|
2412
|
-
"scripts": {
|
|
2413
|
-
"build-css": "node tailwind-to-style/lib/twsx-cli.js"
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
```
|
|
2417
|
-
|
|
2418
|
-
Then run:
|
|
2419
|
-
|
|
2420
|
-
```bash
|
|
2421
|
-
npm run build-css
|
|
2422
|
-
```
|
|
484
|
+
## ð Performance
|
|
2423
485
|
|
|
2424
|
-
|
|
486
|
+
- **Parse & cache** - Styles are cached after first parse
|
|
487
|
+
- **Small bundle** - ~12KB minified (vs 45KB in v2)
|
|
488
|
+
- **No build step** - Instant development workflow
|
|
489
|
+
- **Tree-shakeable** - Only import what you use
|
|
2425
490
|
|
|
2426
|
-
##
|
|
491
|
+
## ð Comparison
|
|
2427
492
|
|
|
2428
|
-
|
|
493
|
+
| Feature | tailwind-to-style | Tailwind CSS | CSS-in-JS |
|
|
494
|
+
|---------|------------------|--------------|-----------|
|
|
495
|
+
| Build Step | â None | â
Required | â None |
|
|
496
|
+
| Bundle Size | ðĒ 12KB | ðĄ ~80KB+ | ðĄ 20-40KB |
|
|
497
|
+
| Runtime | â
Yes | â No | â
Yes |
|
|
498
|
+
| Full Tailwind Support | â
Yes | â
Yes | â No |
|
|
499
|
+
| Framework Agnostic | â
Yes | â
Yes | â ïļ Depends |
|
|
500
|
+
| Nesting Support | â
Yes | â ïļ Plugins | â
Yes |
|
|
501
|
+
| TypeScript | â
Yes | â
Yes | â
Yes |
|
|
2429
502
|
|
|
2430
|
-
|
|
503
|
+
## ð Migration from v2
|
|
2431
504
|
|
|
2432
|
-
[
|
|
505
|
+
See [MIGRATION.md](MIGRATION.md) for detailed migration guide from v2.x to v3.x.
|
|
2433
506
|
|
|
2434
|
-
|
|
507
|
+
**Quick summary:**
|
|
508
|
+
- â
`tws()` - No changes
|
|
509
|
+
- â
`twsx()` - No changes
|
|
510
|
+
- â
`configure()` - No changes
|
|
511
|
+
- â `styled()`, `tv()` - Removed (use emotion/styled-components)
|
|
512
|
+
- â `useTwsx()`, `TwsxProvider` - Removed (use `twsx()` directly)
|
|
513
|
+
- â CLI tools & plugins - Removed (not needed for runtime library)
|
|
2435
514
|
|
|
2436
|
-
|
|
515
|
+
## ðĪ Contributing
|
|
2437
516
|
|
|
2438
|
-
|
|
2439
|
-
- ⥠**New Features**: Continuous improvement based on community feedback
|
|
2440
|
-
- ð **Documentation**: Better examples and tutorials
|
|
2441
|
-
- ð **Performance**: Optimization and new capabilities
|
|
2442
|
-
- ðŽ **Support**: Responsive community support
|
|
517
|
+
Contributions are welcome! Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
2443
518
|
|
|
2444
|
-
|
|
519
|
+
## ð License
|
|
2445
520
|
|
|
2446
|
-
|
|
521
|
+
MIT ÂĐ [Bigetion](https://github.com/Bigetion)
|
|
2447
522
|
|
|
2448
|
-
|
|
523
|
+
## ð Support
|
|
2449
524
|
|
|
2450
|
-
|
|
525
|
+
If you find this library helpful, consider supporting:
|
|
2451
526
|
|
|
2452
|
-
|
|
527
|
+
[â Buy me a coffee](https://buymeacoffee.com/bigetion)
|
|
2453
528
|
|
|
2454
529
|
---
|
|
2455
530
|
|
|
2456
|
-
|
|
531
|
+
**v3.0.0** - Focused, fast, and simple. Just the core. ðŊ
|