tailwind-styled-v4 5.0.26 → 5.0.28
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/LICENSE +21 -21
- package/README.md +575 -575
- package/dist/analyzer.js.map +1 -1
- package/dist/analyzer.mjs.map +1 -1
- package/dist/animate.js.map +1 -1
- package/dist/animate.mjs.map +1 -1
- package/dist/atomic.js.map +1 -1
- package/dist/atomic.mjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +2 -2
- package/dist/cli.mjs.map +1 -1
- package/dist/compiler.js +2 -1
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.mjs +2 -1
- package/dist/compiler.mjs.map +1 -1
- package/dist/dashboard.d.mts +69 -69
- package/dist/dashboard.d.ts +69 -69
- package/dist/dashboard.js.map +1 -1
- package/dist/dashboard.mjs.map +1 -1
- package/dist/devtools.js.map +1 -1
- package/dist/devtools.mjs.map +1 -1
- package/dist/engine.js +26 -5
- package/dist/engine.js.map +1 -1
- package/dist/engine.mjs +26 -5
- package/dist/engine.mjs.map +1 -1
- package/dist/index.browser.mjs +16 -16
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +51 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -28
- package/dist/index.mjs.map +1 -1
- package/dist/next.js +26 -5
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +26 -5
- package/dist/next.mjs.map +1 -1
- package/dist/plugin-api.js.map +1 -1
- package/dist/plugin-api.mjs.map +1 -1
- package/dist/plugin-registry.js.map +1 -1
- package/dist/plugin-registry.mjs.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/preset.js.map +1 -1
- package/dist/preset.mjs.map +1 -1
- package/dist/rspack.js.map +1 -1
- package/dist/rspack.mjs.map +1 -1
- package/dist/runtime-css.js.map +1 -1
- package/dist/runtime-css.mjs.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/dist/runtime.mjs.map +1 -1
- package/dist/scanner.js.map +1 -1
- package/dist/scanner.mjs.map +1 -1
- package/dist/shared.js +26 -5
- package/dist/shared.js.map +1 -1
- package/dist/shared.mjs +26 -5
- package/dist/shared.mjs.map +1 -1
- package/dist/storybook-addon.js.map +1 -1
- package/dist/storybook-addon.mjs.map +1 -1
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs.map +1 -1
- package/dist/syntax.js.map +1 -1
- package/dist/syntax.mjs.map +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/theme.mjs.map +1 -1
- package/dist/turbopackLoader.js +2 -1
- package/dist/turbopackLoader.js.map +1 -1
- package/dist/turbopackLoader.mjs +2 -1
- package/dist/turbopackLoader.mjs.map +1 -1
- package/dist/tw.js +2 -2
- package/dist/tw.js.map +1 -1
- package/dist/tw.mjs +2 -2
- package/dist/tw.mjs.map +1 -1
- package/dist/vite.js +26 -5
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs +26 -5
- package/dist/vite.mjs.map +1 -1
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs.map +1 -1
- package/dist/webpackLoader.js +2 -1
- package/dist/webpackLoader.js.map +1 -1
- package/dist/webpackLoader.mjs +2 -1
- package/dist/webpackLoader.mjs.map +1 -1
- package/native/tailwind-styled-native.node +0 -0
- package/package.json +296 -289
- package/native/tailwind-styled-native.win32-x64-msvc.node +0 -0
package/README.md
CHANGED
|
@@ -1,576 +1,576 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
# tailwind-styled-v4
|
|
4
|
-
|
|
5
|
-
### ⚡ Rust-powered Tailwind CSS v4 untuk React
|
|
6
|
-
**Build-time compiler · Zero runtime overhead · RSC-aware · Next.js / Vite / Rspack**
|
|
7
|
-
|
|
8
|
-
[](https://npmjs.com/package/tailwind-styled-v4)
|
|
9
|
-
[](LICENSE)
|
|
10
|
-
[](https://rust-lang.org)
|
|
11
|
-
[](https://nodejs.org)
|
|
12
|
-
[](#)
|
|
13
|
-
[](https://bundlephobia.com/package/tailwind-styled-v4)
|
|
14
|
-
|
|
15
|
-
</div>
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Apa ini?
|
|
20
|
-
|
|
21
|
-
`tailwind-styled-v4` adalah library styling untuk React yang menggabungkan **DX styled-components** dengan **performa Tailwind CSS v4** dan **engine berbasis Rust**. Tulis komponen dengan `tw.button` atau `tw.div({ variants })` — compiler extract dan optimasi CSS di build time, bukan runtime.
|
|
22
|
-
|
|
23
|
-
**Perbandingan singkat:**
|
|
24
|
-
|
|
25
|
-
| | tailwind-styled-v4 | styled-components | Tailwind CSS biasa |
|
|
26
|
-
|---|---|---|---|
|
|
27
|
-
| Build-time CSS | ✅ | ❌ (runtime inject) | ✅ |
|
|
28
|
-
| Runtime overhead | ~0 | ~15KB | ~0 |
|
|
29
|
-
| Variants API | ✅ type-safe | terbatas | ❌ |
|
|
30
|
-
| SSR/RSC support | ✅ zero config | ⚠️ butuh ServerStyleSheet | ✅ manual |
|
|
31
|
-
| Hydration mismatch | ✅ tidak ada | ⚠️ hash bisa beda | ✅ tidak ada |
|
|
32
|
-
| DevTools readable | ✅ class name jelas | ❌ hash (`sc-abc123`) | ✅ |
|
|
33
|
-
| Engine | 🦀 Rust | JS | JS |
|
|
34
|
-
| Dark mode | ✅ `dark:` prefix | manual | ✅ |
|
|
35
|
-
| TypeScript | ✅ full inference | partial | ✅ |
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## Instalasi
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
npm install tailwind-styled-v4
|
|
43
|
-
|
|
44
|
-
# Setup otomatis
|
|
45
|
-
npx tw setup
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
`npx tw setup` akan otomatis:
|
|
49
|
-
- Mendeteksi bundler (Next.js / Vite / Rspack)
|
|
50
|
-
- Meng-inject plugin ke `next.config.ts` / `vite.config.ts`
|
|
51
|
-
- Membuat `tailwind-styled.config.json` dengan CSS entry yang terdeteksi otomatis
|
|
52
|
-
- Menambahkan `@import "tailwindcss"` ke CSS entry
|
|
53
|
-
- Pre-warming scanner cache supaya dev pertama tidak cache miss
|
|
54
|
-
|
|
55
|
-
---
|
|
56
|
-
|
|
57
|
-
## Quick Start
|
|
58
|
-
|
|
59
|
-
### 1. Template Literal
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
import { tw } from "tailwind-styled-v4"
|
|
63
|
-
|
|
64
|
-
const Button = tw.button`
|
|
65
|
-
inline-flex items-center rounded-lg px-4 py-2
|
|
66
|
-
bg-blue-600 text-white font-medium
|
|
67
|
-
hover:bg-blue-700 transition
|
|
68
|
-
`
|
|
69
|
-
|
|
70
|
-
<Button onClick={handleClick}>Klik saya</Button>
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### 2. Object Config + Variants
|
|
74
|
-
|
|
75
|
-
```tsx
|
|
76
|
-
const Button = tw.button({
|
|
77
|
-
base: "inline-flex items-center rounded-full px-5 py-2 font-medium transition-all",
|
|
78
|
-
variants: {
|
|
79
|
-
intent: {
|
|
80
|
-
primary: "bg-foreground text-background hover:bg-[#383838]",
|
|
81
|
-
secondary: "bg-white text-gray-900 border border-gray-300 hover:bg-gray-50",
|
|
82
|
-
outline: "bg-transparent border-2 border-foreground text-foreground hover:bg-foreground hover:text-background",
|
|
83
|
-
ghost: "bg-transparent text-foreground hover:bg-gray-100",
|
|
84
|
-
},
|
|
85
|
-
size: {
|
|
86
|
-
sm: "h-10 px-4 text-sm rounded-lg",
|
|
87
|
-
md: "h-12 px-5 text-base rounded-full",
|
|
88
|
-
lg: "h-14 px-6 text-lg rounded-full",
|
|
89
|
-
},
|
|
90
|
-
},
|
|
91
|
-
defaultVariants: { intent: "primary", size: "md" },
|
|
92
|
-
states: {
|
|
93
|
-
loading: "opacity-60 cursor-wait pointer-events-none",
|
|
94
|
-
disabled: "opacity-50 cursor-not-allowed",
|
|
95
|
-
fullWidth: "w-full",
|
|
96
|
-
},
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
// TypeScript tahu variant apa yang valid — autocomplete ✅
|
|
100
|
-
<Button intent="primary" size="lg">Submit</Button>
|
|
101
|
-
<Button intent="ghost">Batal</Button>
|
|
102
|
-
<Button intent="outline" size="sm">Edit</Button>
|
|
103
|
-
<Button loading>Memproses...</Button>
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### 3. Sub-components
|
|
107
|
-
|
|
108
|
-
```tsx
|
|
109
|
-
const Card = tw.div({
|
|
110
|
-
base: "rounded-xl bg-white shadow-md overflow-hidden",
|
|
111
|
-
sub: {
|
|
112
|
-
header: "px-6 py-4 border-b font-semibold",
|
|
113
|
-
main: "px-6 py-4",
|
|
114
|
-
footer: "px-6 py-4 border-t text-sm text-gray-400",
|
|
115
|
-
"div:action": "px-6 py-4 flex gap-3", // render <div>, akses Card.action
|
|
116
|
-
},
|
|
117
|
-
states: {
|
|
118
|
-
selected: "ring-2 ring-blue-500",
|
|
119
|
-
disabled: "opacity-50 pointer-events-none",
|
|
120
|
-
},
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
// Penggunaan
|
|
124
|
-
<Card selected>
|
|
125
|
-
<Card.header>Judul Card</Card.header>
|
|
126
|
-
<Card.main>Konten card di sini.</Card.main>
|
|
127
|
-
<Card.action>
|
|
128
|
-
<Button>Lihat Detail</Button>
|
|
129
|
-
<Button intent="ghost">Batal</Button>
|
|
130
|
-
</Card.action>
|
|
131
|
-
<Card.footer>Updated 2 hours ago</Card.footer>
|
|
132
|
-
</Card>
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Format `"tag:name"` untuk sub-components — misalnya `"div:action"` render sebagai `<div>` dengan akses via `Card.action`. TypeScript otomatis strip prefix tag dari type inference.
|
|
136
|
-
|
|
137
|
-
### 4. `.extend()` — Inheritance
|
|
138
|
-
|
|
139
|
-
```tsx
|
|
140
|
-
const PrimaryButton = Button.extend`text-lg px-8`
|
|
141
|
-
const DangerButton = Button.extend({
|
|
142
|
-
classes: "bg-red-600 hover:bg-red-700",
|
|
143
|
-
defaultVariants: { intent: "primary" }
|
|
144
|
-
})
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### 5. States — Boolean Props
|
|
148
|
-
|
|
149
|
-
```tsx
|
|
150
|
-
// states di-resolve via Rust bitmask lookup — O(1), tidak ada runtime overhead
|
|
151
|
-
const Badge = tw.span({
|
|
152
|
-
base: "inline-flex px-2 py-1 rounded text-sm font-medium",
|
|
153
|
-
states: {
|
|
154
|
-
active: "bg-green-100 text-green-800",
|
|
155
|
-
warning: "bg-yellow-100 text-yellow-800",
|
|
156
|
-
error: "bg-red-100 text-red-800",
|
|
157
|
-
},
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
<Badge active>Online</Badge>
|
|
161
|
-
<Badge error>Error</Badge>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### 6. Dark Mode
|
|
165
|
-
|
|
166
|
-
Dark mode bekerja otomatis via `prefers-color-scheme` — tidak perlu konfigurasi tambahan:
|
|
167
|
-
|
|
168
|
-
```tsx
|
|
169
|
-
const Card = tw.div({
|
|
170
|
-
base: "bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100",
|
|
171
|
-
sub: {
|
|
172
|
-
header: "border-b border-gray-200 dark:border-gray-700",
|
|
173
|
-
},
|
|
174
|
-
})
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### 7. Compound Variants
|
|
178
|
-
|
|
179
|
-
```tsx
|
|
180
|
-
const Button = tw.button({
|
|
181
|
-
base: "...",
|
|
182
|
-
variants: {
|
|
183
|
-
intent: { primary: "...", outline: "..." },
|
|
184
|
-
size: { sm: "...", lg: "..." },
|
|
185
|
-
},
|
|
186
|
-
compoundVariants: [
|
|
187
|
-
// Kalau intent=primary AND size=lg → tambah class ini
|
|
188
|
-
{ intent: "primary", size: "lg", class: "shadow-lg" },
|
|
189
|
-
],
|
|
190
|
-
})
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
### 8. .withSub — Strict TypeScript untuk Template Literals
|
|
194
|
-
|
|
195
|
-
```tsx
|
|
196
|
-
const Button = tw.button`
|
|
197
|
-
flex h-12 px-5 rounded-full
|
|
198
|
-
icon { flex h-4 w-4 }
|
|
199
|
-
badge { absolute -top-1 -right-1 }
|
|
200
|
-
`.withSub<"icon" | "badge">()
|
|
201
|
-
|
|
202
|
-
Button.icon // ✅ autocomplete
|
|
203
|
-
Button.badge // ✅ autocomplete
|
|
204
|
-
Button.xyz // ❌ TypeScript error
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
---
|
|
208
|
-
|
|
209
|
-
## Bagaimana CSS Di-generate?
|
|
210
|
-
|
|
211
|
-
Pipeline baru di v5 — tidak lagi pakai empty rules:
|
|
212
|
-
|
|
213
|
-
```
|
|
214
|
-
1. withTailwindStyled (Next.js startup)
|
|
215
|
-
└─> scanWorkspace() via Rust scanner
|
|
216
|
-
└─> ast_extract_classes() per file
|
|
217
|
-
└─> extract semua classes dari variants, states, sub, base
|
|
218
|
-
|
|
219
|
-
2. generateCssForClasses(classes, globals.css)
|
|
220
|
-
└─> Tailwind JS compile(globals.css, { loadStylesheet })
|
|
221
|
-
└─> Tailwind baca @theme inline user (custom colors, fonts, dll)
|
|
222
|
-
└─> Generate real CSS untuk semua classes
|
|
223
|
-
└─> LightningCSS post-process (production only)
|
|
224
|
-
└─> tulis .next/tw-classes/_initial-scan.css
|
|
225
|
-
|
|
226
|
-
3. globals.css: @source "../.next/tw-classes/**"
|
|
227
|
-
└─> Tailwind scan class names dari _initial-scan.css
|
|
228
|
-
└─> Generate CSS di bundle akhir
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
Hasilnya `_initial-scan.css` berisi real CSS (bukan empty rules):
|
|
232
|
-
|
|
233
|
-
```css
|
|
234
|
-
/* tw-classes: initial scan — auto-generated by withTailwindStyled */
|
|
235
|
-
@layer utilities {
|
|
236
|
-
.bg-foreground {
|
|
237
|
-
background-color: var(--foreground);
|
|
238
|
-
}
|
|
239
|
-
.text-foreground {
|
|
240
|
-
color: var(--foreground);
|
|
241
|
-
}
|
|
242
|
-
.hover\:bg-foreground {
|
|
243
|
-
&:hover {
|
|
244
|
-
background-color: var(--foreground);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
/* ... */
|
|
248
|
-
}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
Custom colors dari `@theme inline` di `globals.css` otomatis ter-generate — tidak perlu konfigurasi tambahan.
|
|
252
|
-
|
|
253
|
-
---
|
|
254
|
-
|
|
255
|
-
## Setup Next.js
|
|
256
|
-
|
|
257
|
-
### next.config.ts
|
|
258
|
-
|
|
259
|
-
```ts
|
|
260
|
-
import { withTailwindStyled } from "tailwind-styled-v4/next"
|
|
261
|
-
import type { NextConfig } from "next"
|
|
262
|
-
|
|
263
|
-
const nextConfig: NextConfig = {}
|
|
264
|
-
|
|
265
|
-
export default withTailwindStyled({ verbose: true })(nextConfig)
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### globals.css
|
|
269
|
-
|
|
270
|
-
```css
|
|
271
|
-
@import "tailwindcss";
|
|
272
|
-
@source "../.next/tw-classes/**";
|
|
273
|
-
|
|
274
|
-
:root {
|
|
275
|
-
--background: #ffffff;
|
|
276
|
-
--foreground: #171717;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
@theme inline {
|
|
280
|
-
--color-background: var(--background);
|
|
281
|
-
--color-foreground: var(--foreground);
|
|
282
|
-
--font-sans: var(--font-geist-sans);
|
|
283
|
-
--font-mono: var(--font-geist-mono);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
@media (prefers-color-scheme: dark) {
|
|
287
|
-
:root {
|
|
288
|
-
--background: #0a0a0a;
|
|
289
|
-
--foreground: #ededed;
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
## Bundler Integration
|
|
297
|
-
|
|
298
|
-
### Vite
|
|
299
|
-
|
|
300
|
-
```ts
|
|
301
|
-
import { defineConfig } from "vite"
|
|
302
|
-
import react from "@vitejs/plugin-react"
|
|
303
|
-
import { tailwindStyled } from "tailwind-styled-v4/vite"
|
|
304
|
-
|
|
305
|
-
export default defineConfig({
|
|
306
|
-
plugins: [react(), tailwindStyled()],
|
|
307
|
-
})
|
|
308
|
-
```
|
|
309
|
-
|
|
310
|
-
### Rspack
|
|
311
|
-
|
|
312
|
-
```js
|
|
313
|
-
import { defineConfig } from "@rspack/cli"
|
|
314
|
-
import { tailwindStyled } from "tailwind-styled-v4/rspack"
|
|
315
|
-
|
|
316
|
-
export default defineConfig({
|
|
317
|
-
entry: "./src/index.ts",
|
|
318
|
-
plugins: [tailwindStyled()],
|
|
319
|
-
})
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
---
|
|
323
|
-
|
|
324
|
-
## CLI
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
# Setup otomatis (detect bundler, patch config, pre-warm cache)
|
|
328
|
-
npx tw setup
|
|
329
|
-
|
|
330
|
-
# Verifikasi setup
|
|
331
|
-
npx tw preflight
|
|
332
|
-
|
|
333
|
-
# Analisis workspace
|
|
334
|
-
npx tw audit
|
|
335
|
-
|
|
336
|
-
# Benchmark performa
|
|
337
|
-
npx tw benchmark
|
|
338
|
-
```
|
|
339
|
-
|
|
340
|
-
---
|
|
341
|
-
|
|
342
|
-
## Kenapa Bukan styled-components?
|
|
343
|
-
|
|
344
|
-
styled-components inject `<style>` tag ke DOM saat runtime — setiap component punya hash class (`sc-abc123 dEfGhI`) yang di-generate di browser. Masalahnya:
|
|
345
|
-
|
|
346
|
-
- **Runtime overhead** — ~15KB JS untuk generate + inject CSS
|
|
347
|
-
- **SSR mismatch** — hash bisa berbeda antara server dan client → hydration warning
|
|
348
|
-
- **DevTools susah dibaca** — `sc-abc123` tidak informatif
|
|
349
|
-
- **Butuh setup khusus** — `ServerStyleSheet`, `StyledEngineProvider`, dll untuk Next.js App Router
|
|
350
|
-
|
|
351
|
-
`tailwind-styled-v4` tidak punya masalah ini karena CSS sudah di-bundle sebelum browser buka halaman. Class name readable, SSR dan CSR identik, tidak ada runtime overhead.
|
|
352
|
-
|
|
353
|
-
---
|
|
354
|
-
|
|
355
|
-
## Benchmark
|
|
356
|
-
|
|
357
|
-
Diukur di Node.js 22, Rust 1.75.
|
|
358
|
-
|
|
359
|
-
| Operasi | tailwind-styled-v4 | Tailwind CSS (JS) | Speedup |
|
|
360
|
-
|---|---|---|---|
|
|
361
|
-
| Scan 1000 file | **0.8 ms** | ~340 ms | **~425×** |
|
|
362
|
-
| Compile 500 class | **0.02 ms** | ~1.2 ms | **~60×** |
|
|
363
|
-
| Parse class string | **0.010 ms** | ~0.8 ms | **~80×** |
|
|
364
|
-
| Cache read/write | **0.009 ms** | ~0.5 ms | **~55×** |
|
|
365
|
-
| Watch mode rebuild | **< 5 ms** | ~85 ms | **~17×** |
|
|
366
|
-
|
|
367
|
-
---
|
|
368
|
-
|
|
369
|
-
## Arsitektur
|
|
370
|
-
|
|
371
|
-
```
|
|
372
|
-
tailwind-styled-v4/
|
|
373
|
-
├── native/ # Rust engine (NAPI-RS)
|
|
374
|
-
│ ├── src/application/
|
|
375
|
-
│ │ ├── ast_extract.rs # Extract Tailwind classes dari source files
|
|
376
|
-
│ │ ├── variant_resolver.rs # Variant resolution with precedence
|
|
377
|
-
│ │ ├── variant_system.rs # Variant composition system
|
|
378
|
-
│ │ └── theme_resolver_pool.rs # Multi-tier caching
|
|
379
|
-
│ ├── src/domain/
|
|
380
|
-
│ │ ├── variants.rs # Variant resolution (props override defaults)
|
|
381
|
-
│ │ ├── variant_precedence.rs # Precedence calculation
|
|
382
|
-
│ │ └── transform.rs # Transform object config → JS component
|
|
383
|
-
│ └── src/infrastructure/
|
|
384
|
-
│ ├── napi_bridge_*.rs # Modularized NAPI bridges
|
|
385
|
-
│ └── cache_*.rs # Multi-tier cache backends
|
|
386
|
-
│
|
|
387
|
-
├── packages/
|
|
388
|
-
│ ├── domain/
|
|
389
|
-
│ │ ├── core/ # tw, cx, cv, cn — core API + createComponent
|
|
390
|
-
│ │ ├── compiler/ # Tailwind JS + LightningCSS pipeline
|
|
391
|
-
│ │ └── scanner/ # File scanner (Rust-backed)
|
|
392
|
-
│ ├── presentation/
|
|
393
|
-
│ │ ├── next/ # Next.js plugin (withTailwindStyled)
|
|
394
|
-
│ │ ├── vite/ # Vite plugin
|
|
395
|
-
│ │ └── rspack/ # Rspack plugin
|
|
396
|
-
│ └── infrastructure/
|
|
397
|
-
│ └── cli/ # CLI (tw setup, tw audit, dll)
|
|
398
|
-
│
|
|
399
|
-
├── config/ # Configuration files (centralized)
|
|
400
|
-
│ ├── biome.json
|
|
401
|
-
│ ├── tsconfig.base.json
|
|
402
|
-
│ ├── turbo.json
|
|
403
|
-
│ └── ...
|
|
404
|
-
│
|
|
405
|
-
├── docs/
|
|
406
|
-
│ ├── archive/ # Phase docs, session summaries, reference
|
|
407
|
-
│ ├── phase-4/, phase-5/, phase-6/ # Phase-specific documentation
|
|
408
|
-
│ └── api/ # API reference
|
|
409
|
-
```
|
|
410
|
-
|
|
411
|
-
**New Structure (Phase 7):**
|
|
412
|
-
- Configuration files centralized in `config/` directory
|
|
413
|
-
- Documentation archived in `docs/archive/` for cleaner root
|
|
414
|
-
- Modularized NAPI bridges (`napi_bridge_*.rs`) for better maintainability
|
|
415
|
-
- Phase-specific docs in dedicated directories
|
|
416
|
-
|
|
417
|
-
---
|
|
418
|
-
|
|
419
|
-
## TypeScript
|
|
420
|
-
|
|
421
|
-
Library ini fully typed — tidak ada `any` di public API:
|
|
422
|
-
|
|
423
|
-
```tsx
|
|
424
|
-
// Type inference otomatis dari config
|
|
425
|
-
const Button = tw.button({
|
|
426
|
-
variants: {
|
|
427
|
-
intent: { primary: "...", ghost: "...", outline: "..." },
|
|
428
|
-
size: { sm: "...", md: "...", lg: "..." },
|
|
429
|
-
},
|
|
430
|
-
defaultVariants: { intent: "primary", size: "md" },
|
|
431
|
-
})
|
|
432
|
-
|
|
433
|
-
// TypeScript tahu props yang valid
|
|
434
|
-
<Button intent="invalid" /> // ❌ Type error
|
|
435
|
-
<Button intent="primary" /> // ✅
|
|
436
|
-
|
|
437
|
-
// Sub-components — ExtractSubName type inference
|
|
438
|
-
const Card = tw.div({
|
|
439
|
-
sub: {
|
|
440
|
-
header: "font-bold",
|
|
441
|
-
"div:action": "flex gap-3", // → Card.action (tag prefix di-strip otomatis)
|
|
442
|
-
},
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
Card.action // ✅ autocomplete
|
|
446
|
-
Card.xyz // ❌ TypeScript error
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
---
|
|
450
|
-
|
|
451
|
-
## Environment Variables
|
|
452
|
-
|
|
453
|
-
| Variable | Default | Deskripsi |
|
|
454
|
-
|---|---|---|
|
|
455
|
-
| `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
|
|
456
|
-
| `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
|
|
457
|
-
| `STUDIO_PORT` | `3030` | Port studio server |
|
|
458
|
-
|
|
459
|
-
---
|
|
460
|
-
|
|
461
|
-
## Architecture Updates (Phase 7)
|
|
462
|
-
|
|
463
|
-
### R1-R6: Parser Consolidation through Resolver Caching ✅ Completed 2026-06-12
|
|
464
|
-
|
|
465
|
-
Recent Phase 7 updates span multiple refactoring rounds:
|
|
466
|
-
|
|
467
|
-
**R1: Parser Consolidation** ✅
|
|
468
|
-
- Single unified production parser (v2-based)
|
|
469
|
-
- ~5% binary size reduction
|
|
470
|
-
- 100% backward compatible, all 545+ tests passing
|
|
471
|
-
|
|
472
|
-
**R2-R3: Infrastructure Modularization** ✅
|
|
473
|
-
- NAPI bridge modularized into specialized modules
|
|
474
|
-
- Comprehensive integration tests covering all layers
|
|
475
|
-
- Cache backend infrastructure refactored
|
|
476
|
-
|
|
477
|
-
**R4: Property Testing Framework** ✅
|
|
478
|
-
- 6 core properties verified across 53 test cases
|
|
479
|
-
- Parser determinism property testing
|
|
480
|
-
- Round-trip parsing validation
|
|
481
|
-
- Cache consistency & eviction properties
|
|
482
|
-
|
|
483
|
-
**R5: Variant Precedence System** ✅
|
|
484
|
-
- Native variant resolution with precedence handling
|
|
485
|
-
- Compound variant support
|
|
486
|
-
- Theme-aware variant composition
|
|
487
|
-
|
|
488
|
-
**R6: Resolver Caching** ✅
|
|
489
|
-
- Multi-tier caching for theme resolver
|
|
490
|
-
- Performance optimization verified
|
|
491
|
-
|
|
492
|
-
For architecture details and improvements roadmap, see:
|
|
493
|
-
- [Phase 7 Architecture Design](.kiro/specs/phase-7-architecture/design.md)
|
|
494
|
-
- [R4 Property Tests Design](.kiro/specs/phase-7-architecture/R4_PROPERTY_TESTS_DESIGN.md)
|
|
495
|
-
- [R5 Variant Precedence Design](.kiro/specs/phase-7-architecture/R5_VARIANT_PRECEDENCE_DESIGN.md)
|
|
496
|
-
- [Full spec directory](.kiro/specs/phase-7-architecture/)
|
|
497
|
-
|
|
498
|
-
---
|
|
499
|
-
|
|
500
|
-
## Recent Changes (82 commits, not yet pushed)
|
|
501
|
-
|
|
502
|
-
### Infrastructure & Organization 🏗️
|
|
503
|
-
- **Config centralization** — moved 6 root config files to `config/` directory
|
|
504
|
-
- **Docs reorganization** — 42 summary/reference files moved to `docs/archive/`
|
|
505
|
-
- **Benchmarking suite** — added comprehensive performance benchmarking (`native/benches/`)
|
|
506
|
-
|
|
507
|
-
### Rust Engine Enhancements 🦀
|
|
508
|
-
- **Modularized NAPI bridges** — split monolithic bridge into specialized modules:
|
|
509
|
-
- `napi_bridge_cache.rs` — caching layer
|
|
510
|
-
- `napi_bridge_redis.rs` — distributed cache
|
|
511
|
-
- `napi_bridge_theme.rs` — theme resolution
|
|
512
|
-
- `napi_bridge_variants.rs` — variant composition
|
|
513
|
-
- And 5 more specialized modules
|
|
514
|
-
- **Theme resolver pool** — multi-tier caching with adaptive strategies
|
|
515
|
-
- **Variant system improvements** — precedence calculation, compound variant support
|
|
516
|
-
|
|
517
|
-
### Testing & Validation ✅
|
|
518
|
-
- **Property testing** — 6 core properties across 53 test cases
|
|
519
|
-
- **Integration tests** — comprehensive NAPI module tests (1000+ tests added)
|
|
520
|
-
- **Variant precedence tests** — 493+ test cases for variant resolution
|
|
521
|
-
- **Performance benchmarks** — week8, week9 scale testing suites
|
|
522
|
-
|
|
523
|
-
### TypeScript/JS Improvements
|
|
524
|
-
- **Native bridge refactor** — simplified async/sync patterns, improved error handling
|
|
525
|
-
- **Cache integration** — unified cache interface across Redis, LRU, and file-based backends
|
|
526
|
-
- **Stream support** — added streaming CSS compilation
|
|
527
|
-
|
|
528
|
-
### Documentation 📚
|
|
529
|
-
- **Phase 7 specs** — comprehensive design docs in `.kiro/specs/phase-7-architecture/`
|
|
530
|
-
- **NAPI module guide** — `MIGRATION_GUIDE_PHASE_7_3.md`
|
|
531
|
-
- **Architecture docs** — modular bridge patterns and integration guide
|
|
532
|
-
|
|
533
|
-
---
|
|
534
|
-
|
|
535
|
-
```bash
|
|
536
|
-
git clone https://github.com/Dictionar32/tailwind-styled-v4.git
|
|
537
|
-
cd tailwind-styled-v4
|
|
538
|
-
|
|
539
|
-
npm install
|
|
540
|
-
|
|
541
|
-
# Build Rust binary + semua packages
|
|
542
|
-
npm run build
|
|
543
|
-
|
|
544
|
-
# Build Rust only
|
|
545
|
-
npm run build:rust
|
|
546
|
-
|
|
547
|
-
# Test
|
|
548
|
-
npm run test
|
|
549
|
-
|
|
550
|
-
# Dev mode
|
|
551
|
-
npm run dev
|
|
552
|
-
|
|
553
|
-
# Benchmark
|
|
554
|
-
npm run bench
|
|
555
|
-
```
|
|
556
|
-
|
|
557
|
-
**Requirements:**
|
|
558
|
-
- Node.js 20+
|
|
559
|
-
- Rust 1.75+ (untuk build dari source)
|
|
560
|
-
|
|
561
|
-
---
|
|
562
|
-
|
|
563
|
-
## Contributing
|
|
564
|
-
|
|
565
|
-
PR dan issue sangat welcome!
|
|
566
|
-
|
|
567
|
-
Prioritas saat ini:
|
|
568
|
-
- [ ] macOS & Windows pre-built binary
|
|
569
|
-
- [ ] Docs website (VitePress)
|
|
570
|
-
- [ ] More bundler adapters
|
|
571
|
-
|
|
572
|
-
---
|
|
573
|
-
|
|
574
|
-
## License
|
|
575
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# tailwind-styled-v4
|
|
4
|
+
|
|
5
|
+
### ⚡ Rust-powered Tailwind CSS v4 untuk React
|
|
6
|
+
**Build-time compiler · Zero runtime overhead · RSC-aware · Next.js / Vite / Rspack**
|
|
7
|
+
|
|
8
|
+
[](https://npmjs.com/package/tailwind-styled-v4)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://rust-lang.org)
|
|
11
|
+
[](https://nodejs.org)
|
|
12
|
+
[](#)
|
|
13
|
+
[](https://bundlephobia.com/package/tailwind-styled-v4)
|
|
14
|
+
|
|
15
|
+
</div>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Apa ini?
|
|
20
|
+
|
|
21
|
+
`tailwind-styled-v4` adalah library styling untuk React yang menggabungkan **DX styled-components** dengan **performa Tailwind CSS v4** dan **engine berbasis Rust**. Tulis komponen dengan `tw.button` atau `tw.div({ variants })` — compiler extract dan optimasi CSS di build time, bukan runtime.
|
|
22
|
+
|
|
23
|
+
**Perbandingan singkat:**
|
|
24
|
+
|
|
25
|
+
| | tailwind-styled-v4 | styled-components | Tailwind CSS biasa |
|
|
26
|
+
|---|---|---|---|
|
|
27
|
+
| Build-time CSS | ✅ | ❌ (runtime inject) | ✅ |
|
|
28
|
+
| Runtime overhead | ~0 | ~15KB | ~0 |
|
|
29
|
+
| Variants API | ✅ type-safe | terbatas | ❌ |
|
|
30
|
+
| SSR/RSC support | ✅ zero config | ⚠️ butuh ServerStyleSheet | ✅ manual |
|
|
31
|
+
| Hydration mismatch | ✅ tidak ada | ⚠️ hash bisa beda | ✅ tidak ada |
|
|
32
|
+
| DevTools readable | ✅ class name jelas | ❌ hash (`sc-abc123`) | ✅ |
|
|
33
|
+
| Engine | 🦀 Rust | JS | JS |
|
|
34
|
+
| Dark mode | ✅ `dark:` prefix | manual | ✅ |
|
|
35
|
+
| TypeScript | ✅ full inference | partial | ✅ |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Instalasi
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install tailwind-styled-v4
|
|
43
|
+
|
|
44
|
+
# Setup otomatis
|
|
45
|
+
npx tw setup
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`npx tw setup` akan otomatis:
|
|
49
|
+
- Mendeteksi bundler (Next.js / Vite / Rspack)
|
|
50
|
+
- Meng-inject plugin ke `next.config.ts` / `vite.config.ts`
|
|
51
|
+
- Membuat `tailwind-styled.config.json` dengan CSS entry yang terdeteksi otomatis
|
|
52
|
+
- Menambahkan `@import "tailwindcss"` ke CSS entry
|
|
53
|
+
- Pre-warming scanner cache supaya dev pertama tidak cache miss
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
### 1. Template Literal
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
import { tw } from "tailwind-styled-v4"
|
|
63
|
+
|
|
64
|
+
const Button = tw.button`
|
|
65
|
+
inline-flex items-center rounded-lg px-4 py-2
|
|
66
|
+
bg-blue-600 text-white font-medium
|
|
67
|
+
hover:bg-blue-700 transition
|
|
68
|
+
`
|
|
69
|
+
|
|
70
|
+
<Button onClick={handleClick}>Klik saya</Button>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. Object Config + Variants
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
const Button = tw.button({
|
|
77
|
+
base: "inline-flex items-center rounded-full px-5 py-2 font-medium transition-all",
|
|
78
|
+
variants: {
|
|
79
|
+
intent: {
|
|
80
|
+
primary: "bg-foreground text-background hover:bg-[#383838]",
|
|
81
|
+
secondary: "bg-white text-gray-900 border border-gray-300 hover:bg-gray-50",
|
|
82
|
+
outline: "bg-transparent border-2 border-foreground text-foreground hover:bg-foreground hover:text-background",
|
|
83
|
+
ghost: "bg-transparent text-foreground hover:bg-gray-100",
|
|
84
|
+
},
|
|
85
|
+
size: {
|
|
86
|
+
sm: "h-10 px-4 text-sm rounded-lg",
|
|
87
|
+
md: "h-12 px-5 text-base rounded-full",
|
|
88
|
+
lg: "h-14 px-6 text-lg rounded-full",
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
defaultVariants: { intent: "primary", size: "md" },
|
|
92
|
+
states: {
|
|
93
|
+
loading: "opacity-60 cursor-wait pointer-events-none",
|
|
94
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
95
|
+
fullWidth: "w-full",
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
// TypeScript tahu variant apa yang valid — autocomplete ✅
|
|
100
|
+
<Button intent="primary" size="lg">Submit</Button>
|
|
101
|
+
<Button intent="ghost">Batal</Button>
|
|
102
|
+
<Button intent="outline" size="sm">Edit</Button>
|
|
103
|
+
<Button loading>Memproses...</Button>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 3. Sub-components
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
const Card = tw.div({
|
|
110
|
+
base: "rounded-xl bg-white shadow-md overflow-hidden",
|
|
111
|
+
sub: {
|
|
112
|
+
header: "px-6 py-4 border-b font-semibold",
|
|
113
|
+
main: "px-6 py-4",
|
|
114
|
+
footer: "px-6 py-4 border-t text-sm text-gray-400",
|
|
115
|
+
"div:action": "px-6 py-4 flex gap-3", // render <div>, akses Card.action
|
|
116
|
+
},
|
|
117
|
+
states: {
|
|
118
|
+
selected: "ring-2 ring-blue-500",
|
|
119
|
+
disabled: "opacity-50 pointer-events-none",
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// Penggunaan
|
|
124
|
+
<Card selected>
|
|
125
|
+
<Card.header>Judul Card</Card.header>
|
|
126
|
+
<Card.main>Konten card di sini.</Card.main>
|
|
127
|
+
<Card.action>
|
|
128
|
+
<Button>Lihat Detail</Button>
|
|
129
|
+
<Button intent="ghost">Batal</Button>
|
|
130
|
+
</Card.action>
|
|
131
|
+
<Card.footer>Updated 2 hours ago</Card.footer>
|
|
132
|
+
</Card>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Format `"tag:name"` untuk sub-components — misalnya `"div:action"` render sebagai `<div>` dengan akses via `Card.action`. TypeScript otomatis strip prefix tag dari type inference.
|
|
136
|
+
|
|
137
|
+
### 4. `.extend()` — Inheritance
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
const PrimaryButton = Button.extend`text-lg px-8`
|
|
141
|
+
const DangerButton = Button.extend({
|
|
142
|
+
classes: "bg-red-600 hover:bg-red-700",
|
|
143
|
+
defaultVariants: { intent: "primary" }
|
|
144
|
+
})
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### 5. States — Boolean Props
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// states di-resolve via Rust bitmask lookup — O(1), tidak ada runtime overhead
|
|
151
|
+
const Badge = tw.span({
|
|
152
|
+
base: "inline-flex px-2 py-1 rounded text-sm font-medium",
|
|
153
|
+
states: {
|
|
154
|
+
active: "bg-green-100 text-green-800",
|
|
155
|
+
warning: "bg-yellow-100 text-yellow-800",
|
|
156
|
+
error: "bg-red-100 text-red-800",
|
|
157
|
+
},
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
<Badge active>Online</Badge>
|
|
161
|
+
<Badge error>Error</Badge>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 6. Dark Mode
|
|
165
|
+
|
|
166
|
+
Dark mode bekerja otomatis via `prefers-color-scheme` — tidak perlu konfigurasi tambahan:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
const Card = tw.div({
|
|
170
|
+
base: "bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100",
|
|
171
|
+
sub: {
|
|
172
|
+
header: "border-b border-gray-200 dark:border-gray-700",
|
|
173
|
+
},
|
|
174
|
+
})
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 7. Compound Variants
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
const Button = tw.button({
|
|
181
|
+
base: "...",
|
|
182
|
+
variants: {
|
|
183
|
+
intent: { primary: "...", outline: "..." },
|
|
184
|
+
size: { sm: "...", lg: "..." },
|
|
185
|
+
},
|
|
186
|
+
compoundVariants: [
|
|
187
|
+
// Kalau intent=primary AND size=lg → tambah class ini
|
|
188
|
+
{ intent: "primary", size: "lg", class: "shadow-lg" },
|
|
189
|
+
],
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 8. .withSub — Strict TypeScript untuk Template Literals
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
const Button = tw.button`
|
|
197
|
+
flex h-12 px-5 rounded-full
|
|
198
|
+
icon { flex h-4 w-4 }
|
|
199
|
+
badge { absolute -top-1 -right-1 }
|
|
200
|
+
`.withSub<"icon" | "badge">()
|
|
201
|
+
|
|
202
|
+
Button.icon // ✅ autocomplete
|
|
203
|
+
Button.badge // ✅ autocomplete
|
|
204
|
+
Button.xyz // ❌ TypeScript error
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Bagaimana CSS Di-generate?
|
|
210
|
+
|
|
211
|
+
Pipeline baru di v5 — tidak lagi pakai empty rules:
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
1. withTailwindStyled (Next.js startup)
|
|
215
|
+
└─> scanWorkspace() via Rust scanner
|
|
216
|
+
└─> ast_extract_classes() per file
|
|
217
|
+
└─> extract semua classes dari variants, states, sub, base
|
|
218
|
+
|
|
219
|
+
2. generateCssForClasses(classes, globals.css)
|
|
220
|
+
└─> Tailwind JS compile(globals.css, { loadStylesheet })
|
|
221
|
+
└─> Tailwind baca @theme inline user (custom colors, fonts, dll)
|
|
222
|
+
└─> Generate real CSS untuk semua classes
|
|
223
|
+
└─> LightningCSS post-process (production only)
|
|
224
|
+
└─> tulis .next/tw-classes/_initial-scan.css
|
|
225
|
+
|
|
226
|
+
3. globals.css: @source "../.next/tw-classes/**"
|
|
227
|
+
└─> Tailwind scan class names dari _initial-scan.css
|
|
228
|
+
└─> Generate CSS di bundle akhir
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Hasilnya `_initial-scan.css` berisi real CSS (bukan empty rules):
|
|
232
|
+
|
|
233
|
+
```css
|
|
234
|
+
/* tw-classes: initial scan — auto-generated by withTailwindStyled */
|
|
235
|
+
@layer utilities {
|
|
236
|
+
.bg-foreground {
|
|
237
|
+
background-color: var(--foreground);
|
|
238
|
+
}
|
|
239
|
+
.text-foreground {
|
|
240
|
+
color: var(--foreground);
|
|
241
|
+
}
|
|
242
|
+
.hover\:bg-foreground {
|
|
243
|
+
&:hover {
|
|
244
|
+
background-color: var(--foreground);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/* ... */
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Custom colors dari `@theme inline` di `globals.css` otomatis ter-generate — tidak perlu konfigurasi tambahan.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Setup Next.js
|
|
256
|
+
|
|
257
|
+
### next.config.ts
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
import { withTailwindStyled } from "tailwind-styled-v4/next"
|
|
261
|
+
import type { NextConfig } from "next"
|
|
262
|
+
|
|
263
|
+
const nextConfig: NextConfig = {}
|
|
264
|
+
|
|
265
|
+
export default withTailwindStyled({ verbose: true })(nextConfig)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### globals.css
|
|
269
|
+
|
|
270
|
+
```css
|
|
271
|
+
@import "tailwindcss";
|
|
272
|
+
@source "../.next/tw-classes/**";
|
|
273
|
+
|
|
274
|
+
:root {
|
|
275
|
+
--background: #ffffff;
|
|
276
|
+
--foreground: #171717;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@theme inline {
|
|
280
|
+
--color-background: var(--background);
|
|
281
|
+
--color-foreground: var(--foreground);
|
|
282
|
+
--font-sans: var(--font-geist-sans);
|
|
283
|
+
--font-mono: var(--font-geist-mono);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@media (prefers-color-scheme: dark) {
|
|
287
|
+
:root {
|
|
288
|
+
--background: #0a0a0a;
|
|
289
|
+
--foreground: #ededed;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Bundler Integration
|
|
297
|
+
|
|
298
|
+
### Vite
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import { defineConfig } from "vite"
|
|
302
|
+
import react from "@vitejs/plugin-react"
|
|
303
|
+
import { tailwindStyled } from "tailwind-styled-v4/vite"
|
|
304
|
+
|
|
305
|
+
export default defineConfig({
|
|
306
|
+
plugins: [react(), tailwindStyled()],
|
|
307
|
+
})
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Rspack
|
|
311
|
+
|
|
312
|
+
```js
|
|
313
|
+
import { defineConfig } from "@rspack/cli"
|
|
314
|
+
import { tailwindStyled } from "tailwind-styled-v4/rspack"
|
|
315
|
+
|
|
316
|
+
export default defineConfig({
|
|
317
|
+
entry: "./src/index.ts",
|
|
318
|
+
plugins: [tailwindStyled()],
|
|
319
|
+
})
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## CLI
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
# Setup otomatis (detect bundler, patch config, pre-warm cache)
|
|
328
|
+
npx tw setup
|
|
329
|
+
|
|
330
|
+
# Verifikasi setup
|
|
331
|
+
npx tw preflight
|
|
332
|
+
|
|
333
|
+
# Analisis workspace
|
|
334
|
+
npx tw audit
|
|
335
|
+
|
|
336
|
+
# Benchmark performa
|
|
337
|
+
npx tw benchmark
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Kenapa Bukan styled-components?
|
|
343
|
+
|
|
344
|
+
styled-components inject `<style>` tag ke DOM saat runtime — setiap component punya hash class (`sc-abc123 dEfGhI`) yang di-generate di browser. Masalahnya:
|
|
345
|
+
|
|
346
|
+
- **Runtime overhead** — ~15KB JS untuk generate + inject CSS
|
|
347
|
+
- **SSR mismatch** — hash bisa berbeda antara server dan client → hydration warning
|
|
348
|
+
- **DevTools susah dibaca** — `sc-abc123` tidak informatif
|
|
349
|
+
- **Butuh setup khusus** — `ServerStyleSheet`, `StyledEngineProvider`, dll untuk Next.js App Router
|
|
350
|
+
|
|
351
|
+
`tailwind-styled-v4` tidak punya masalah ini karena CSS sudah di-bundle sebelum browser buka halaman. Class name readable, SSR dan CSR identik, tidak ada runtime overhead.
|
|
352
|
+
|
|
353
|
+
---
|
|
354
|
+
|
|
355
|
+
## Benchmark
|
|
356
|
+
|
|
357
|
+
Diukur di Node.js 22, Rust 1.75.
|
|
358
|
+
|
|
359
|
+
| Operasi | tailwind-styled-v4 | Tailwind CSS (JS) | Speedup |
|
|
360
|
+
|---|---|---|---|
|
|
361
|
+
| Scan 1000 file | **0.8 ms** | ~340 ms | **~425×** |
|
|
362
|
+
| Compile 500 class | **0.02 ms** | ~1.2 ms | **~60×** |
|
|
363
|
+
| Parse class string | **0.010 ms** | ~0.8 ms | **~80×** |
|
|
364
|
+
| Cache read/write | **0.009 ms** | ~0.5 ms | **~55×** |
|
|
365
|
+
| Watch mode rebuild | **< 5 ms** | ~85 ms | **~17×** |
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Arsitektur
|
|
370
|
+
|
|
371
|
+
```
|
|
372
|
+
tailwind-styled-v4/
|
|
373
|
+
├── native/ # Rust engine (NAPI-RS)
|
|
374
|
+
│ ├── src/application/
|
|
375
|
+
│ │ ├── ast_extract.rs # Extract Tailwind classes dari source files
|
|
376
|
+
│ │ ├── variant_resolver.rs # Variant resolution with precedence
|
|
377
|
+
│ │ ├── variant_system.rs # Variant composition system
|
|
378
|
+
│ │ └── theme_resolver_pool.rs # Multi-tier caching
|
|
379
|
+
│ ├── src/domain/
|
|
380
|
+
│ │ ├── variants.rs # Variant resolution (props override defaults)
|
|
381
|
+
│ │ ├── variant_precedence.rs # Precedence calculation
|
|
382
|
+
│ │ └── transform.rs # Transform object config → JS component
|
|
383
|
+
│ └── src/infrastructure/
|
|
384
|
+
│ ├── napi_bridge_*.rs # Modularized NAPI bridges
|
|
385
|
+
│ └── cache_*.rs # Multi-tier cache backends
|
|
386
|
+
│
|
|
387
|
+
├── packages/
|
|
388
|
+
│ ├── domain/
|
|
389
|
+
│ │ ├── core/ # tw, cx, cv, cn — core API + createComponent
|
|
390
|
+
│ │ ├── compiler/ # Tailwind JS + LightningCSS pipeline
|
|
391
|
+
│ │ └── scanner/ # File scanner (Rust-backed)
|
|
392
|
+
│ ├── presentation/
|
|
393
|
+
│ │ ├── next/ # Next.js plugin (withTailwindStyled)
|
|
394
|
+
│ │ ├── vite/ # Vite plugin
|
|
395
|
+
│ │ └── rspack/ # Rspack plugin
|
|
396
|
+
│ └── infrastructure/
|
|
397
|
+
│ └── cli/ # CLI (tw setup, tw audit, dll)
|
|
398
|
+
│
|
|
399
|
+
├── config/ # Configuration files (centralized)
|
|
400
|
+
│ ├── biome.json
|
|
401
|
+
│ ├── tsconfig.base.json
|
|
402
|
+
│ ├── turbo.json
|
|
403
|
+
│ └── ...
|
|
404
|
+
│
|
|
405
|
+
├── docs/
|
|
406
|
+
│ ├── archive/ # Phase docs, session summaries, reference
|
|
407
|
+
│ ├── phase-4/, phase-5/, phase-6/ # Phase-specific documentation
|
|
408
|
+
│ └── api/ # API reference
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
**New Structure (Phase 7):**
|
|
412
|
+
- Configuration files centralized in `config/` directory
|
|
413
|
+
- Documentation archived in `docs/archive/` for cleaner root
|
|
414
|
+
- Modularized NAPI bridges (`napi_bridge_*.rs`) for better maintainability
|
|
415
|
+
- Phase-specific docs in dedicated directories
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## TypeScript
|
|
420
|
+
|
|
421
|
+
Library ini fully typed — tidak ada `any` di public API:
|
|
422
|
+
|
|
423
|
+
```tsx
|
|
424
|
+
// Type inference otomatis dari config
|
|
425
|
+
const Button = tw.button({
|
|
426
|
+
variants: {
|
|
427
|
+
intent: { primary: "...", ghost: "...", outline: "..." },
|
|
428
|
+
size: { sm: "...", md: "...", lg: "..." },
|
|
429
|
+
},
|
|
430
|
+
defaultVariants: { intent: "primary", size: "md" },
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
// TypeScript tahu props yang valid
|
|
434
|
+
<Button intent="invalid" /> // ❌ Type error
|
|
435
|
+
<Button intent="primary" /> // ✅
|
|
436
|
+
|
|
437
|
+
// Sub-components — ExtractSubName type inference
|
|
438
|
+
const Card = tw.div({
|
|
439
|
+
sub: {
|
|
440
|
+
header: "font-bold",
|
|
441
|
+
"div:action": "flex gap-3", // → Card.action (tag prefix di-strip otomatis)
|
|
442
|
+
},
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
Card.action // ✅ autocomplete
|
|
446
|
+
Card.xyz // ❌ TypeScript error
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
## Environment Variables
|
|
452
|
+
|
|
453
|
+
| Variable | Default | Deskripsi |
|
|
454
|
+
|---|---|---|
|
|
455
|
+
| `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
|
|
456
|
+
| `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
|
|
457
|
+
| `STUDIO_PORT` | `3030` | Port studio server |
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Architecture Updates (Phase 7)
|
|
462
|
+
|
|
463
|
+
### R1-R6: Parser Consolidation through Resolver Caching ✅ Completed 2026-06-12
|
|
464
|
+
|
|
465
|
+
Recent Phase 7 updates span multiple refactoring rounds:
|
|
466
|
+
|
|
467
|
+
**R1: Parser Consolidation** ✅
|
|
468
|
+
- Single unified production parser (v2-based)
|
|
469
|
+
- ~5% binary size reduction
|
|
470
|
+
- 100% backward compatible, all 545+ tests passing
|
|
471
|
+
|
|
472
|
+
**R2-R3: Infrastructure Modularization** ✅
|
|
473
|
+
- NAPI bridge modularized into specialized modules
|
|
474
|
+
- Comprehensive integration tests covering all layers
|
|
475
|
+
- Cache backend infrastructure refactored
|
|
476
|
+
|
|
477
|
+
**R4: Property Testing Framework** ✅
|
|
478
|
+
- 6 core properties verified across 53 test cases
|
|
479
|
+
- Parser determinism property testing
|
|
480
|
+
- Round-trip parsing validation
|
|
481
|
+
- Cache consistency & eviction properties
|
|
482
|
+
|
|
483
|
+
**R5: Variant Precedence System** ✅
|
|
484
|
+
- Native variant resolution with precedence handling
|
|
485
|
+
- Compound variant support
|
|
486
|
+
- Theme-aware variant composition
|
|
487
|
+
|
|
488
|
+
**R6: Resolver Caching** ✅
|
|
489
|
+
- Multi-tier caching for theme resolver
|
|
490
|
+
- Performance optimization verified
|
|
491
|
+
|
|
492
|
+
For architecture details and improvements roadmap, see:
|
|
493
|
+
- [Phase 7 Architecture Design](.kiro/specs/phase-7-architecture/design.md)
|
|
494
|
+
- [R4 Property Tests Design](.kiro/specs/phase-7-architecture/R4_PROPERTY_TESTS_DESIGN.md)
|
|
495
|
+
- [R5 Variant Precedence Design](.kiro/specs/phase-7-architecture/R5_VARIANT_PRECEDENCE_DESIGN.md)
|
|
496
|
+
- [Full spec directory](.kiro/specs/phase-7-architecture/)
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## Recent Changes (82 commits, not yet pushed)
|
|
501
|
+
|
|
502
|
+
### Infrastructure & Organization 🏗️
|
|
503
|
+
- **Config centralization** — moved 6 root config files to `config/` directory
|
|
504
|
+
- **Docs reorganization** — 42 summary/reference files moved to `docs/archive/`
|
|
505
|
+
- **Benchmarking suite** — added comprehensive performance benchmarking (`native/benches/`)
|
|
506
|
+
|
|
507
|
+
### Rust Engine Enhancements 🦀
|
|
508
|
+
- **Modularized NAPI bridges** — split monolithic bridge into specialized modules:
|
|
509
|
+
- `napi_bridge_cache.rs` — caching layer
|
|
510
|
+
- `napi_bridge_redis.rs` — distributed cache
|
|
511
|
+
- `napi_bridge_theme.rs` — theme resolution
|
|
512
|
+
- `napi_bridge_variants.rs` — variant composition
|
|
513
|
+
- And 5 more specialized modules
|
|
514
|
+
- **Theme resolver pool** — multi-tier caching with adaptive strategies
|
|
515
|
+
- **Variant system improvements** — precedence calculation, compound variant support
|
|
516
|
+
|
|
517
|
+
### Testing & Validation ✅
|
|
518
|
+
- **Property testing** — 6 core properties across 53 test cases
|
|
519
|
+
- **Integration tests** — comprehensive NAPI module tests (1000+ tests added)
|
|
520
|
+
- **Variant precedence tests** — 493+ test cases for variant resolution
|
|
521
|
+
- **Performance benchmarks** — week8, week9 scale testing suites
|
|
522
|
+
|
|
523
|
+
### TypeScript/JS Improvements
|
|
524
|
+
- **Native bridge refactor** — simplified async/sync patterns, improved error handling
|
|
525
|
+
- **Cache integration** — unified cache interface across Redis, LRU, and file-based backends
|
|
526
|
+
- **Stream support** — added streaming CSS compilation
|
|
527
|
+
|
|
528
|
+
### Documentation 📚
|
|
529
|
+
- **Phase 7 specs** — comprehensive design docs in `.kiro/specs/phase-7-architecture/`
|
|
530
|
+
- **NAPI module guide** — `MIGRATION_GUIDE_PHASE_7_3.md`
|
|
531
|
+
- **Architecture docs** — modular bridge patterns and integration guide
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
```bash
|
|
536
|
+
git clone https://github.com/Dictionar32/tailwind-styled-v4.git
|
|
537
|
+
cd tailwind-styled-v4
|
|
538
|
+
|
|
539
|
+
npm install
|
|
540
|
+
|
|
541
|
+
# Build Rust binary + semua packages
|
|
542
|
+
npm run build
|
|
543
|
+
|
|
544
|
+
# Build Rust only
|
|
545
|
+
npm run build:rust
|
|
546
|
+
|
|
547
|
+
# Test
|
|
548
|
+
npm run test
|
|
549
|
+
|
|
550
|
+
# Dev mode
|
|
551
|
+
npm run dev
|
|
552
|
+
|
|
553
|
+
# Benchmark
|
|
554
|
+
npm run bench
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Requirements:**
|
|
558
|
+
- Node.js 20+
|
|
559
|
+
- Rust 1.75+ (untuk build dari source)
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## Contributing
|
|
564
|
+
|
|
565
|
+
PR dan issue sangat welcome!
|
|
566
|
+
|
|
567
|
+
Prioritas saat ini:
|
|
568
|
+
- [ ] macOS & Windows pre-built binary
|
|
569
|
+
- [ ] Docs website (VitePress)
|
|
570
|
+
- [ ] More bundler adapters
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## License
|
|
575
|
+
|
|
576
576
|
[MIT](LICENSE) © Dictionar32
|