tailwind-styled-v4 5.0.10 → 5.0.11

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.
Files changed (93) hide show
  1. package/README.md +245 -373
  2. package/dist/analyzer.js +75 -22
  3. package/dist/analyzer.js.map +1 -1
  4. package/dist/analyzer.mjs +74 -21
  5. package/dist/analyzer.mjs.map +1 -1
  6. package/dist/animate.js +4 -2
  7. package/dist/animate.js.map +1 -1
  8. package/dist/animate.mjs +4 -2
  9. package/dist/animate.mjs.map +1 -1
  10. package/dist/atomic.js +20 -5
  11. package/dist/atomic.js.map +1 -1
  12. package/dist/atomic.mjs +20 -5
  13. package/dist/atomic.mjs.map +1 -1
  14. package/dist/cli.js +174 -67
  15. package/dist/cli.js.map +1 -1
  16. package/dist/cli.mjs +171 -64
  17. package/dist/cli.mjs.map +1 -1
  18. package/dist/compiler.d.mts +7 -1
  19. package/dist/compiler.d.ts +7 -1
  20. package/dist/compiler.js +53 -27
  21. package/dist/compiler.js.map +1 -1
  22. package/dist/compiler.mjs +53 -27
  23. package/dist/compiler.mjs.map +1 -1
  24. package/dist/devtools.js.map +1 -1
  25. package/dist/devtools.mjs.map +1 -1
  26. package/dist/engine.js +159 -61
  27. package/dist/engine.js.map +1 -1
  28. package/dist/engine.mjs +159 -61
  29. package/dist/engine.mjs.map +1 -1
  30. package/dist/index.browser.mjs +1512 -0
  31. package/dist/index.browser.mjs.map +1 -0
  32. package/dist/index.d.mts +94 -12
  33. package/dist/index.d.ts +94 -12
  34. package/dist/index.js +436 -106
  35. package/dist/index.js.map +1 -1
  36. package/dist/index.mjs +436 -106
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/next.js +1946 -47
  39. package/dist/next.js.map +1 -1
  40. package/dist/next.mjs +1929 -44
  41. package/dist/next.mjs.map +1 -1
  42. package/dist/plugin-api.js.map +1 -1
  43. package/dist/plugin-api.mjs.map +1 -1
  44. package/dist/plugin-registry.js +23 -10
  45. package/dist/plugin-registry.js.map +1 -1
  46. package/dist/plugin-registry.mjs +23 -11
  47. package/dist/plugin-registry.mjs.map +1 -1
  48. package/dist/plugin.js.map +1 -1
  49. package/dist/plugin.mjs.map +1 -1
  50. package/dist/rspack.js.map +1 -1
  51. package/dist/rspack.mjs.map +1 -1
  52. package/dist/scanner.js +72 -19
  53. package/dist/scanner.js.map +1 -1
  54. package/dist/scanner.mjs +71 -18
  55. package/dist/scanner.mjs.map +1 -1
  56. package/dist/shared.js +32 -15
  57. package/dist/shared.js.map +1 -1
  58. package/dist/shared.mjs +32 -15
  59. package/dist/shared.mjs.map +1 -1
  60. package/dist/svelte.js +38 -12
  61. package/dist/svelte.js.map +1 -1
  62. package/dist/svelte.mjs +38 -12
  63. package/dist/svelte.mjs.map +1 -1
  64. package/dist/syntax.js +17 -5
  65. package/dist/syntax.js.map +1 -1
  66. package/dist/syntax.mjs +17 -5
  67. package/dist/syntax.mjs.map +1 -1
  68. package/dist/theme.js +4 -2
  69. package/dist/theme.js.map +1 -1
  70. package/dist/theme.mjs +4 -2
  71. package/dist/theme.mjs.map +1 -1
  72. package/dist/turbopackLoader.js +87 -33
  73. package/dist/turbopackLoader.js.map +1 -1
  74. package/dist/turbopackLoader.mjs +87 -33
  75. package/dist/turbopackLoader.mjs.map +1 -1
  76. package/dist/tw.js +174 -67
  77. package/dist/tw.js.map +1 -1
  78. package/dist/tw.mjs +171 -64
  79. package/dist/tw.mjs.map +1 -1
  80. package/dist/vite.js +145 -63
  81. package/dist/vite.js.map +1 -1
  82. package/dist/vite.mjs +145 -63
  83. package/dist/vite.mjs.map +1 -1
  84. package/dist/vue.js +38 -12
  85. package/dist/vue.js.map +1 -1
  86. package/dist/vue.mjs +38 -12
  87. package/dist/vue.mjs.map +1 -1
  88. package/dist/webpackLoader.js +20 -5
  89. package/dist/webpackLoader.js.map +1 -1
  90. package/dist/webpackLoader.mjs +20 -5
  91. package/dist/webpackLoader.mjs.map +1 -1
  92. package/native/tailwind-styled-native.node +0 -0
  93. package/package.json +29 -24
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # tailwind-styled-v4
4
4
 
5
- ### ⚡ Rust-powered Tailwind CSS untuk React
5
+ ### ⚡ Rust-powered Tailwind CSS v4 untuk React
6
6
  **Build-time compiler · Zero runtime overhead · RSC-aware · Next.js / Vite / Rspack**
7
7
 
8
8
  [![npm](https://img.shields.io/npm/v/tailwind-styled-v4?color=blue)](https://npmjs.com/package/tailwind-styled-v4)
@@ -18,35 +18,39 @@
18
18
 
19
19
  ## Apa ini?
20
20
 
21
- `tailwind-styled-v4` adalah library styling untuk React yang menggabungkan **utility-first** Tailwind CSS dengan **engine berbasis Rust**. Tulis komponen dengan `tw.button` atau `tw.div({ variants })` — compiler extract dan optimasi CSS di build time, bukan runtime.
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
22
 
23
23
  **Perbandingan singkat:**
24
24
 
25
25
  | | tailwind-styled-v4 | styled-components | Tailwind CSS biasa |
26
26
  |---|---|---|---|
27
- | Build-time CSS | ✅ | ❌ | ✅ |
27
+ | Build-time CSS | ✅ | ❌ (runtime inject) | ✅ |
28
28
  | Runtime overhead | ~0 | ~15KB | ~0 |
29
- | Variants API | ✅ | terbatas | ❌ |
30
- | RSC support | ✅ otomatis | | ✅ manual |
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`) | ✅ |
31
33
  | Engine | 🦀 Rust | JS | JS |
34
+ | Dark mode | ✅ `dark:` prefix | manual | ✅ |
35
+ | TypeScript | ✅ full inference | partial | ✅ |
32
36
 
33
37
  ---
34
38
 
35
39
  ## Instalasi
36
40
 
37
41
  ```bash
38
- # npm
39
42
  npm install tailwind-styled-v4
40
43
 
41
- # Lalu jalankan setup otomatis
44
+ # Setup otomatis
42
45
  npx tw setup
43
46
  ```
44
47
 
45
48
  `npx tw setup` akan otomatis:
46
49
  - Mendeteksi bundler (Next.js / Vite / Rspack)
47
50
  - Meng-inject plugin ke `next.config.ts` / `vite.config.ts`
48
- - Membuat `tailwind-styled.config.json`
51
+ - Membuat `tailwind-styled.config.json` dengan CSS entry yang terdeteksi otomatis
49
52
  - Menambahkan `@import "tailwindcss"` ke CSS entry
53
+ - Pre-warming scanner cache supaya dev pertama tidak cache miss
50
54
 
51
55
  ---
52
56
 
@@ -63,7 +67,6 @@ const Button = tw.button`
63
67
  hover:bg-blue-700 transition
64
68
  `
65
69
 
66
- // Pakai seperti komponen biasa
67
70
  <Button onClick={handleClick}>Klik saya</Button>
68
71
  ```
69
72
 
@@ -71,302 +74,233 @@ const Button = tw.button`
71
74
 
72
75
  ```tsx
73
76
  const Button = tw.button({
74
- base: "inline-flex items-center rounded-lg px-4 py-2 font-medium transition",
77
+ base: "inline-flex items-center rounded-full px-5 py-2 font-medium transition-all",
75
78
  variants: {
76
79
  intent: {
77
- primary: "bg-blue-600 text-white hover:bg-blue-700",
78
- secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200",
79
- danger: "bg-red-600 text-white hover:bg-red-700",
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",
80
84
  },
81
85
  size: {
82
- sm: "text-sm px-3 py-1.5",
83
- md: "text-base px-4 py-2",
84
- lg: "text-lg px-6 py-3",
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",
85
89
  },
86
90
  },
87
91
  defaultVariants: { intent: "primary", size: "md" },
88
- })
89
-
90
- // TypeScript tahu variant apa yang valid
91
- <Button intent="primary" size="lg">Submit</Button>
92
- <Button intent="danger">Hapus</Button>
93
- ```
94
-
95
- ### 3. `.extend()` — Inheritance
96
-
97
- ```tsx
98
- // Turunan dari Button, tambah styling tanpa override base
99
- const IconButton = Button.extend`
100
- aspect-square justify-center rounded-full p-2
101
- `
102
-
103
- const LoadingButton = Button.extend`
104
- opacity-60 cursor-wait
105
- `
106
- ```
107
-
108
- ### 4. `cx()` — Conditional Class Merge
109
-
110
- ```tsx
111
- import { cx } from "tailwind-styled-v4"
112
-
113
- function StatusDot({ online }: { online: boolean }) {
114
- return (
115
- <span className={cx(
116
- "h-2.5 w-2.5 rounded-full",
117
- online ? "bg-green-500" : "bg-gray-300"
118
- )} />
119
- )
120
- }
121
- ```
122
-
123
- ### 5. `cv()` — Class Variants (headless)
124
-
125
- ```tsx
126
- import { cv } from "tailwind-styled-v4"
127
-
128
- // Tanpa element HTML — untuk komponen custom
129
- const alertStyles = cv({
130
- base: "rounded-lg border p-4 text-sm",
131
- variants: {
132
- type: {
133
- info: "border-blue-200 bg-blue-50 text-blue-800",
134
- success: "border-green-200 bg-green-50 text-green-800",
135
- error: "border-red-200 bg-red-50 text-red-800",
136
- },
92
+ states: {
93
+ loading: "opacity-60 cursor-wait pointer-events-none",
94
+ disabled: "opacity-50 cursor-not-allowed",
95
+ fullWidth: "w-full",
137
96
  },
138
97
  })
139
98
 
140
- function Alert({ type, children }) {
141
- return <div className={alertStyles({ type })}>{children}</div>
142
- }
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>
143
104
  ```
144
105
 
145
- ### 6. Sub-components
146
-
147
- Ada **dua cara** mendefinisikan sub-components:
148
-
149
- #### A. Config Object — Direkomendasikan (Autocomplete + Type Safe)
106
+ ### 3. Sub-components
150
107
 
151
108
  ```tsx
152
109
  const Card = tw.div({
153
- base: "flex flex-col p-4 rounded-xl bg-white shadow",
110
+ base: "rounded-xl bg-white shadow-md overflow-hidden",
154
111
  sub: {
155
- header: "font-bold text-lg border-b pb-2",
156
- body: "text-gray-600 py-2",
157
- footer: "border-t pt-2 text-sm text-gray-400",
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",
158
120
  },
159
121
  })
160
122
 
161
- // TypeScript infer keys dari object literal → autocomplete penuh
162
- <Card>
163
- <Card.header>Judul</Card.header> // ✅ autocomplete
164
- <Card.body>Konten</Card.body> // ✅ autocomplete
165
- <Card.footer>Footer</Card.footer> // ✅ autocomplete
166
- <Card.xyz>?</Card.xyz> // ❌ TypeScript error
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>
167
132
  </Card>
168
133
  ```
169
134
 
170
- #### B. Template Literal Inline Ringkas (tanpa autocomplete)
171
-
172
- ```tsx
173
- const Card = tw.div`
174
- flex flex-col p-4 rounded-xl bg-white shadow
175
- [header] { font-bold text-lg border-b pb-2 }
176
- [body] { text-gray-600 py-2 }
177
- [footer] { border-t pt-2 text-sm text-gray-400 }
178
- `
179
-
180
- // Runtime benar, tapi TypeScript tidak bisa infer nama dari multiline template —
181
- // ini limitasi TypeScript, bukan bug library. Gunakan config object untuk type safety.
182
- <Card>
183
- <Card.header>Judul</Card.header>
184
- <Card.body>Konten</Card.body>
185
- <Card.footer>Footer</Card.footer>
186
- </Card>
187
- ```
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.
188
136
 
189
- > Sub-components **tidak mewarisi** style base. Untuk mewarisi, pakai `.extend()` (lihat Pattern B di bawah).
190
-
191
- > **Sub-component tidak terdefinisi** tidak akan crash — library otomatis fallback ke `<span>` passthrough. Tapi tetap gunakan config object untuk catch typo di TypeScript.
192
-
193
- ### 7. State Engine — Zero-JS State Management
137
+ ### 4. `.extend()` Inheritance
194
138
 
195
139
  ```tsx
196
- // Tanpa React state — pakai data attribute langsung
197
- const Button = tw.button({
198
- base: "transition transform",
199
- state: {
200
- active: "bg-blue-600 scale-95",
201
- loading: "opacity-70 cursor-wait",
202
- disabled: "opacity-50 cursor-not-allowed",
203
- },
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" }
204
144
  })
205
- // Usage: <Button data-active="true">Click</Button>
206
- // Ketika data-active="true", style aktif otomatis tanpa re-render
207
145
  ```
208
146
 
209
- ### 8. Container Query Engine
147
+ ### 5. States Boolean Props
210
148
 
211
149
  ```tsx
212
- const Card = tw.div({
213
- container: {
214
- sm: "flex-col",
215
- md: "flex-row",
216
- lg: "grid grid-cols-2",
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",
217
157
  },
218
158
  })
219
- // Auto-generate container queries, tidak perlu tulis @container sendiri
220
- ```
221
-
222
- ### 9. Live Token Engine — Dynamic CSS Variables
223
159
 
224
- ```tsx
225
- import { liveToken } from "tailwind-styled-v4"
226
-
227
- const tokens = liveToken({
228
- primary: "#3b82f6",
229
- secondary: "#64748b",
230
- radius: "0.5rem",
231
- })
232
- // Auto-generate CSS variables: --tw-primary, --tw-secondary, dll
233
- // Subscribe perubahan: tokens.subscribe(callback)
160
+ <Badge active>Online</Badge>
161
+ <Badge error>Error</Badge>
234
162
  ```
235
163
 
236
- ### 10. tw.server — RSC-only Components
164
+ ### 6. Dark Mode
237
165
 
238
- ```tsx
239
- // tw.server adalah namespace terpisah — pakai tw.server.tagname
240
- const Avatar = tw.server.img`rounded-full object-cover`
241
- const Hero = tw.server.section`py-24 text-center`
242
-
243
- // Bisa pakai sub-components juga
244
- const Card = tw.server.div`
245
- p-4 rounded-xl shadow
246
- [header] { font-bold text-lg }
247
- [body] { text-gray-600 }
248
- `
249
-
250
- // Di browser (dev): otomatis log warning kalau ter-render di client
251
- // Di production: silent, tidak ada overhead
252
- ```
253
-
254
- ### 11. Component Wrapping — tw(ExistingComponent)
255
-
256
- ```tsx
257
- // Wrap komponen manapun dengan styling tambahan
258
- const StyledCard = tw(Card)`shadow-lg border`
259
- const BigButton = tw(Button)`text-xl px-8 py-4`
260
- // Bisa juga pakai .extend():
261
- const IconButton = Button.extend`p-2 rounded-full`
262
- ```
263
-
264
- ---
265
-
266
- ### Pattern Sub-components
267
-
268
- **Pattern A: Config Object — Direkomendasikan (autocomplete + type safe)**
166
+ Dark mode bekerja otomatis via `prefers-color-scheme` — tidak perlu konfigurasi tambahan:
269
167
 
270
168
  ```tsx
271
169
  const Card = tw.div({
272
- base: "flex flex-col p-4 rounded-xl bg-white shadow",
170
+ base: "bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100",
273
171
  sub: {
274
- header: "font-bold text-lg border-b pb-2",
275
- body: "text-gray-600 py-2",
276
- footer: "border-t pt-2 text-sm",
172
+ header: "border-b border-gray-200 dark:border-gray-700",
277
173
  },
278
174
  })
279
- // TypeScript infer: Card.header, Card.body, Card.footer ✅ autocomplete
280
- // Card.xyz → TypeScript error ✅
281
175
  ```
282
176
 
283
- **Pattern A2: Inline Template `[name] { }` — Ringkas (tanpa autocomplete)**
177
+ ### 7. Compound Variants
284
178
 
285
179
  ```tsx
286
- const Card = tw.div`
287
- flex flex-col p-4 rounded-xl bg-white shadow
288
- [header] { font-bold text-lg border-b pb-2 }
289
- [body] { text-gray-600 py-2 }
290
- [footer] { border-t pt-2 text-sm }
291
- `
292
- // Runtime benar — TypeScript tidak bisa infer nama dari multiline template literal
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
+ })
293
191
  ```
294
192
 
295
- **Pattern B: `.extend()`Sub-components Mewarisi Style Base**
296
-
297
- Pakai ini kalau sub-components butuh semua style dari parent:
193
+ ### 8. .withSubStrict TypeScript untuk Template Literals
298
194
 
299
195
  ```tsx
300
- const Card = tw.div`flex flex-col p-4 bg-white shadow`
301
- const CardHeader = Card.extend`font-bold text-lg border-b`
302
- const CardBody = Card.extend`text-gray-600`
303
- const CardFooter = Card.extend`border-t pt-4 text-sm`
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
304
205
  ```
305
206
 
306
- **Pattern C: tw() Wrapper**
307
-
308
- Mirip `.extend()` tapi untuk wrapping komponen yang sudah ada:
207
+ ---
309
208
 
310
- ```tsx
311
- const Card = tw.div`flex flex-col p-4 bg-white shadow`
312
- const CardHeader = tw(Card)`font-bold text-lg`
313
- const CardBody = tw(Card)`text-gray-600`
314
- const CardFooter = tw(Card)`border-t pt-4`
315
- ```
209
+ ## Bagaimana CSS Di-generate?
316
210
 
317
- **Pattern D: Base Variable**
211
+ Pipeline baru di v5 — tidak lagi pakai empty rules:
318
212
 
319
- Kalau butuh share classes ke komponen yang benar-benar independen:
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
+ ```
320
230
 
321
- ```tsx
322
- const base = "flex flex-col p-4 rounded-xl"
323
- const Card = tw.div`${base} bg-white shadow`
324
- const CardHeader = tw.div`${base} font-bold`
325
- const CardBody = tw.div`${base} text-gray-600`
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
+ }
326
249
  ```
327
250
 
251
+ Custom colors dari `@theme inline` di `globals.css` otomatis ter-generate — tidak perlu konfigurasi tambahan.
252
+
328
253
  ---
329
254
 
330
- ## Bundler Integration
255
+ ## Setup Next.js
331
256
 
332
- ### Next.js (App Router)
257
+ ### next.config.ts
333
258
 
334
259
  ```ts
335
- // next.config.ts
260
+ import { withTailwindStyled } from "tailwind-styled-v4/next"
336
261
  import type { NextConfig } from "next"
337
- import { withTailwindStyled } from "@tailwind-styled/next"
338
262
 
339
- const nextConfig: NextConfig = {
340
- reactStrictMode: true,
341
- }
263
+ const nextConfig: NextConfig = {}
342
264
 
343
- export default withTailwindStyled(nextConfig)
265
+ export default withTailwindStyled({ verbose: true })(nextConfig)
344
266
  ```
345
267
 
346
- ```tsx
347
- // app/page.tsx — Server Component, tidak perlu 'use client'
348
- import { tw } from "tailwind-styled-v4"
268
+ ### globals.css
349
269
 
350
- // Compiler otomatis deteksi RSC boundary
351
- const Hero = tw.section`py-24 text-center`
352
- const Title = tw.h1`text-5xl font-bold text-gray-900`
270
+ ```css
271
+ @import "tailwindcss";
272
+ @source "../.next/tw-classes/**";
353
273
 
354
- export default function Page() {
355
- return (
356
- <Hero>
357
- <Title>Hello from RSC</Title>
358
- </Hero>
359
- )
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
+ }
360
291
  }
361
292
  ```
362
293
 
294
+ ---
295
+
296
+ ## Bundler Integration
297
+
363
298
  ### Vite
364
299
 
365
300
  ```ts
366
- // vite.config.ts
367
301
  import { defineConfig } from "vite"
368
302
  import react from "@vitejs/plugin-react"
369
- import { tailwindStyled } from "@tailwind-styled/vite"
303
+ import { tailwindStyled } from "tailwind-styled-v4/vite"
370
304
 
371
305
  export default defineConfig({
372
306
  plugins: [react(), tailwindStyled()],
@@ -376,9 +310,8 @@ export default defineConfig({
376
310
  ### Rspack
377
311
 
378
312
  ```js
379
- // rspack.config.mjs
380
313
  import { defineConfig } from "@rspack/cli"
381
- import { tailwindStyled } from "@tailwind-styled/rspack"
314
+ import { tailwindStyled } from "tailwind-styled-v4/rspack"
382
315
 
383
316
  export default defineConfig({
384
317
  entry: "./src/index.ts",
@@ -391,7 +324,7 @@ export default defineConfig({
391
324
  ## CLI
392
325
 
393
326
  ```bash
394
- # Setup otomatis (interaktif pilih project type)
327
+ # Setup otomatis (detect bundler, patch config, pre-warm cache)
395
328
  npx tw setup
396
329
 
397
330
  # Verifikasi setup
@@ -406,9 +339,22 @@ npx tw benchmark
406
339
 
407
340
  ---
408
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
+
409
355
  ## Benchmark
410
356
 
411
- Diukur di Debian Linux, Node.js 22, Rust 1.75.
357
+ Diukur di Node.js 22, Rust 1.75.
412
358
 
413
359
  | Operasi | tailwind-styled-v4 | Tailwind CSS (JS) | Speedup |
414
360
  |---|---|---|---|
@@ -418,59 +364,94 @@ Diukur di Debian Linux, Node.js 22, Rust 1.75.
418
364
  | Cache read/write | **0.009 ms** | ~0.5 ms | **~55×** |
419
365
  | Watch mode rebuild | **< 5 ms** | ~85 ms | **~17×** |
420
366
 
421
- *Benchmark dijalankan via `npm run bench` (scripts/benchmark/run.mjs)*
422
-
423
367
  ---
424
368
 
425
369
  ## Arsitektur
426
370
 
427
371
  ```
428
372
  tailwind-styled-v4/
429
- ├── native/ # Rust engine (N-API, 27 functions)
430
- │ ├── src/lib.rs # Entry, 27 N-API exports
431
- ├── src/oxc_parser.rs # Oxc AST + regex hybrid parser
432
- │ ├── src/scan_cache.rs # DashMap in-memory cache
433
- └── src/watcher.rs # notify v6 file watcher
373
+ ├── native/ # Rust engine (NAPI-RS)
374
+ │ ├── src/application/
375
+ │ └── ast_extract.rs # Extract Tailwind classes dari source files
376
+ │ ├── src/domain/
377
+ │ ├── variants.rs # Variant resolution (props override defaults)
378
+ │ │ └── transform.rs # Transform object config → JS component
379
+ │ └── src/infrastructure/
380
+ │ └── cache_store.rs # Persistent cache dengan bracket-aware parser
434
381
 
435
382
  ├── packages/
436
- │ ├── core/ # tw, cx, cv, cn — core API
437
- │ ├── compiler/ # AST transform, CSS generation
438
- │ ├── scanner/ # File scanner (Rust-backed)
439
- ├── engine/ # Build engine, incremental diff
440
- │ ├── animate/ # Animation DSL
441
- ├── theme/ # Multi-theme engine
442
- ├── plugin/ # Plugin system
443
- ├── cli/ # CLI (tw setup, tw audit, dll)
444
- │ ├── next/ # Next.js adapter
445
- │ ├── vite/ # Vite adapter
446
- │ └── rspack/ # Rspack adapter
447
-
448
- └── examples/
449
- ├── vite/ # Vite demo
450
- ├── vite-react/ # Vite React demo (dark mode)
451
- ├── standar-config-next-js-app/ # Next.js App Router demo
452
- └── rspack/ # Rspack + Node.js demo
383
+ │ ├── domain/
384
+ ├── core/ # tw, cx, cv, cn — core API + createComponent
385
+ ├── compiler/ # Tailwind JS + LightningCSS pipeline
386
+ │ └── scanner/ # File scanner (Rust-backed)
387
+ │ ├── presentation/
388
+ │ └── next/ # Next.js plugin (withTailwindStyled)
389
+ └── infrastructure/
390
+ └── cli/ # CLI (tw setup, tw audit, dll)
391
+ ```
392
+
393
+ ---
394
+
395
+ ## TypeScript
396
+
397
+ Library ini fully typed tidak ada `any` di public API:
398
+
399
+ ```tsx
400
+ // Type inference otomatis dari config
401
+ const Button = tw.button({
402
+ variants: {
403
+ intent: { primary: "...", ghost: "...", outline: "..." },
404
+ size: { sm: "...", md: "...", lg: "..." },
405
+ },
406
+ defaultVariants: { intent: "primary", size: "md" },
407
+ })
408
+
409
+ // TypeScript tahu props yang valid
410
+ <Button intent="invalid" /> // ❌ Type error
411
+ <Button intent="primary" /> // ✅
412
+
413
+ // Sub-components — ExtractSubName type inference
414
+ const Card = tw.div({
415
+ sub: {
416
+ header: "font-bold",
417
+ "div:action": "flex gap-3", // → Card.action (tag prefix di-strip otomatis)
418
+ },
419
+ })
420
+
421
+ Card.action // ✅ autocomplete
422
+ Card.xyz // ❌ TypeScript error
453
423
  ```
454
424
 
455
425
  ---
456
426
 
427
+ ## Environment Variables
428
+
429
+ | Variable | Default | Deskripsi |
430
+ |---|---|---|
431
+ | `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
432
+ | `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
433
+ | `STUDIO_PORT` | `3030` | Port studio server |
434
+
435
+ ---
436
+
457
437
  ## Development
458
438
 
459
439
  ```bash
460
- # Clone
461
440
  git clone https://github.com/Dictionar32/tailwind-styled-v4.git
462
441
  cd tailwind-styled-v4
463
442
 
464
- # Install dependencies
465
443
  npm install
466
444
 
467
445
  # Build Rust binary + semua packages
468
446
  npm run build
469
447
 
470
- # Test (Rust + JS)
448
+ # Build Rust only
449
+ npm run build:rust
450
+
451
+ # Test
471
452
  npm run test
472
453
 
473
- # Dev mode (watch all packages)
454
+ # Dev mode
474
455
  npm run dev
475
456
 
476
457
  # Benchmark
@@ -480,129 +461,20 @@ npm run bench
480
461
  **Requirements:**
481
462
  - Node.js 20+
482
463
  - Rust 1.75+ (untuk build dari source)
483
- - Pre-built binary sudah disertakan untuk Linux x64
484
464
 
485
465
  ---
486
466
 
487
467
  ## Contributing
488
468
 
489
- PR dan issue sangat welcome! Baca [CONTRIBUTING.md](CONTRIBUTING.md) untuk panduan.
469
+ PR dan issue sangat welcome!
490
470
 
491
471
  Prioritas saat ini:
492
- - [x] macOS & Windows pre-built binary *(planned — see RELEASE.md)*
493
- - [x] Docs website *(planned — see RELEASE.md)*
472
+ - [ ] macOS & Windows pre-built binary
473
+ - [ ] Docs website (VitePress)
474
+ - [ ] More bundler adapters
494
475
 
495
476
  ---
496
477
 
497
478
  ## License
498
479
 
499
- [MIT](LICENSE) © Dictionar32
500
-
501
- ---
502
-
503
- ## Engine Architecture (v4.5 Platform Overhaul)
504
-
505
- Sprint 6–10 membawa perombakan arsitektur besar pada engine. Berikut ringkasannya.
506
-
507
- ### 🦀 Rust-backed pipeline
508
-
509
- | Komponen | Implementasi | Fallback |
510
- |---|---|---|
511
- | Class scanner | `scan_workspace` (Rust) | JS `scanWorkspace` |
512
- | Persistent cache | `cache_read/write` (Rust) | JS `ScanCache` |
513
- | Incremental diff | `process_file_change` + DashMap | JS class set diff |
514
- | File watcher | `notify` crate via polling IPC | Node.js `fs.watch` |
515
- | Class analyzer | `analyze_classes` (Rust) | — |
516
-
517
- ### ⚡ Performance
518
-
519
- - Cold start scan: **<10ms** (was >100ms) via persistent Rust cache
520
- - Incremental update: **~0ms** untuk unchanged files
521
- - Watch idle CPU: **~66% reduction** (500ms poll interval)
522
- - Cache hit rate: typically **>95%** pada incremental dev
523
-
524
- ### 🔌 Platform Adapters
525
-
526
- Semua adapter (Next.js, Vite, Rspack) sekarang **bundle compiler secara inline** — user tidak perlu menginstall `@tailwind-styled/compiler` secara terpisah.
527
-
528
- ```ts
529
- // next.config.ts
530
- import { withTailwindStyled } from 'tailwind-styled-v4/next'
531
- export default withTailwindStyled()(nextConfig)
532
-
533
- // vite.config.ts
534
- import { tailwindStyledPlugin } from 'tailwind-styled-v4/vite'
535
- export default defineConfig({ plugins: [tailwindStyledPlugin()] })
536
- ```
537
-
538
- `preserveImports: true` diset di semua loader — `cv`, `cx`, `cn` tetap tersedia di output.
539
-
540
- ### 📊 Developer Tooling
541
-
542
- ```bash
543
- # Analisis workspace (Rust engine)
544
- tw analyze .
545
- tw stats .
546
-
547
- # Dashboard metrics real-time
548
- tw dashboard
549
-
550
- # Watch mode incremental
551
- tw watch .
552
- ```
553
-
554
- DevTools overlay (`Ctrl+Shift+D`) menampilkan:
555
- - **Inspector** — hover element → lihat variant props & classes
556
- - **State** — reactive state components
557
- - **Container** — container query breakpoints aktif
558
- - **Tokens** — live token editor (perubahan instan)
559
- - **Analyzer** — DOM scan + engine metrics dari dashboard
560
-
561
- ### 🖥️ Studio Desktop (Electron)
562
-
563
- ```bash
564
- # Dev mode
565
- npm run dev -w @tailwind-styled/studio-desktop
566
-
567
- # Build distribusi
568
- npm run build:mac # macOS DMG + ZIP
569
- npm run build:win # Windows NSIS installer
570
- npm run build:linux # AppImage + DEB
571
- npm run build:all # Semua platform sekaligus
572
- ```
573
-
574
- Engine tersedia langsung dari renderer via `window.studioDesktop`:
575
-
576
- ```js
577
- const result = await window.studioDesktop.engineBuild()
578
- // { ok: true, totalFiles: 42, uniqueClasses: 312, cssLength: 18540 }
579
-
580
- window.studioDesktop.onEngineEvent((event) => {
581
- if (event.type === 'change') console.log('Rebuilt:', event.result.css.length, 'bytes')
582
- })
583
- await window.studioDesktop.engineWatchStart()
584
- ```
585
-
586
- ### 🧪 Testing
587
-
588
- ```ts
589
- import { expectEngineMetrics, toHaveEngineMetrics, tailwindMatchersWithMetrics } from '@tailwind-styled/testing'
590
-
591
- // Assertion langsung
592
- expectEngineMetrics(metrics, { minFiles: 10, maxBuildTimeMs: 500, cacheHitRateMin: 0.9 })
593
-
594
- // Vitest/Jest custom matcher
595
- expect.extend(tailwindMatchersWithMetrics)
596
- expect(metrics).toHaveEngineMetrics({ minFiles: 5 })
597
- ```
598
-
599
- ### 🔧 Environment Variables
600
-
601
- | Variable | Default | Deskripsi |
602
- |---|---|---|
603
- | `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
604
- | `TWS_NO_NATIVE` | `0` | `1` = paksa JS fallback |
605
- | `TWS_NO_RUST` | `0` | Alias untuk `TWS_NO_NATIVE` |
606
- | `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
607
- | `STUDIO_PORT` | `3030` | Port studio server |
608
- | `STUDIO_VERBOSE` | tidak ada | Tampilkan stdout/stderr studio server |
480
+ [MIT](LICENSE) © Dictionar32