tailwind-styled-v4 5.0.35 → 5.0.37
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 +458 -339
- package/dist/analyzer.d.mts +152 -5
- package/dist/analyzer.d.ts +152 -5
- package/dist/animate.d.mts +30 -3
- package/dist/animate.d.ts +30 -3
- package/dist/cli.js +9 -6
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +9 -6
- package/dist/cli.mjs.map +1 -1
- package/dist/compiler.d.mts +5 -0
- package/dist/compiler.d.ts +5 -0
- package/dist/compiler.js.map +1 -1
- package/dist/compiler.mjs.map +1 -1
- package/dist/devtools.d.mts +88 -2
- package/dist/devtools.d.ts +88 -2
- package/dist/engine.d.mts +557 -6
- package/dist/engine.d.ts +557 -6
- package/dist/engine.js.map +1 -1
- package/dist/engine.mjs.map +1 -1
- package/dist/index.browser.mjs +689 -508
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.d.mts +236 -9
- package/dist/index.d.ts +236 -9
- package/dist/index.js +38 -20
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +38 -20
- package/dist/index.mjs.map +1 -1
- package/dist/next.js +240 -170
- package/dist/next.js.map +1 -1
- package/dist/next.mjs +240 -170
- package/dist/next.mjs.map +1 -1
- package/dist/plugin-api.d.mts +4 -2
- package/dist/plugin-api.d.ts +4 -2
- package/dist/plugin-registry.js +39 -25
- package/dist/plugin-registry.js.map +1 -1
- package/dist/plugin-registry.mjs +39 -25
- package/dist/plugin-registry.mjs.map +1 -1
- package/dist/plugin.d.mts +165 -5
- package/dist/plugin.d.ts +165 -5
- package/dist/plugin.js +17 -0
- package/dist/plugin.js.map +1 -1
- package/dist/plugin.mjs +15 -1
- package/dist/plugin.mjs.map +1 -1
- package/dist/runtime.d.mts +38 -3
- package/dist/runtime.d.ts +38 -3
- package/dist/scanner.d.mts +58 -4
- package/dist/scanner.d.ts +58 -4
- package/dist/shared.d.mts +185 -3
- package/dist/shared.d.ts +185 -3
- package/dist/shared.js.map +1 -1
- package/dist/shared.mjs.map +1 -1
- package/dist/storybook-addon.d.mts +2 -2
- package/dist/storybook-addon.d.ts +2 -2
- package/dist/svelte.d.mts +2 -1
- package/dist/svelte.d.ts +2 -1
- package/dist/svelte.js +4 -4
- package/dist/svelte.js.map +1 -1
- package/dist/svelte.mjs +4 -4
- package/dist/svelte.mjs.map +1 -1
- package/dist/theme.d.mts +38 -3
- package/dist/theme.d.ts +38 -3
- package/dist/turbopackLoader.js.map +1 -1
- package/dist/turbopackLoader.mjs.map +1 -1
- package/dist/tw.js +9 -6
- package/dist/tw.js.map +1 -1
- package/dist/tw.mjs +9 -6
- package/dist/tw.mjs.map +1 -1
- package/dist/vite.js.map +1 -1
- package/dist/vite.mjs.map +1 -1
- package/dist/vue.d.mts +4 -1
- package/dist/vue.d.ts +4 -1
- package/dist/vue.js +4 -4
- package/dist/vue.js.map +1 -1
- package/dist/vue.mjs +4 -4
- package/dist/vue.mjs.map +1 -1
- package/native/tailwind-styled-native.linux-x64-gnu.node +0 -0
- package/native/tailwind-styled-native.node +0 -0
- package/package.json +7 -5
- package/dist/analyzeWorkspace-B1_XRfdl.d.ts +0 -134
- package/dist/analyzeWorkspace-hYfu4Hg3.d.mts +0 -134
- package/dist/index-DQI6O24n.d.mts +0 -464
- package/dist/index-NDINUhLN.d.mts +0 -90
- package/dist/index-NDINUhLN.d.ts +0 -90
- package/dist/index-UkYbyBkR.d.ts +0 -464
- package/dist/liveTokenEngine-CN9ian1R.d.ts +0 -38
- package/dist/liveTokenEngine-DKoWRtqH.d.mts +0 -38
- package/dist/schemas-DR-SLxZZ.d.mts +0 -59
- package/dist/schemas-DR-SLxZZ.d.ts +0 -59
- package/dist/trace-Dz4vmZdy.d.mts +0 -96
- package/dist/trace-Dz4vmZdy.d.ts +0 -96
- package/dist/types-DXr2PmGP.d.mts +0 -31
- package/dist/types-DXr2PmGP.d.ts +0 -31
package/README.md
CHANGED
|
@@ -2,37 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
# tailwind-styled-v4
|
|
4
4
|
|
|
5
|
-
### ⚡ Rust-powered
|
|
6
|
-
**Build-time compiler · Zero runtime overhead ·
|
|
5
|
+
### ⚡ Rust-powered CSS-in-JS untuk React
|
|
6
|
+
**Build-time compiler · Zero runtime overhead · Type-safe variants · RSC-ready**
|
|
7
7
|
|
|
8
8
|
[](https://npmjs.com/package/tailwind-styled-v4)
|
|
9
9
|
[](LICENSE)
|
|
10
10
|
[](https://rust-lang.org)
|
|
11
11
|
[](https://nodejs.org)
|
|
12
|
-
[](#)
|
|
13
13
|
[](https://bundlephobia.com/package/tailwind-styled-v4)
|
|
14
14
|
|
|
15
15
|
</div>
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
`tailwind-styled-v4` adalah library styling React yang menggabungkan **DX styled-components** dengan **performa Tailwind CSS v4** — dikompilasi oleh engine Rust. Tulis komponen sekali dengan `tw.button({ variants })`, Rust extract dan optimasi seluruh CSS di build time.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
**Perbandingan:**
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
| |
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
| Engine | 🦀 Rust | JS | JS |
|
|
34
|
-
| Dark mode | ✅ `dark:` prefix | manual | ✅ |
|
|
35
|
-
| TypeScript | ✅ full inference | partial | ✅ |
|
|
23
|
+
| | tailwind-styled-v4 | styled-components | Tailwind biasa | Panda CSS |
|
|
24
|
+
|---|---|---|---|---|
|
|
25
|
+
| Build-time CSS | ✅ | ❌ runtime inject | ✅ | ✅ |
|
|
26
|
+
| Runtime JS | ~0 | ~15KB | ~0 | ~0 |
|
|
27
|
+
| Variants API | ✅ type-safe | terbatas | ❌ | ✅ |
|
|
28
|
+
| SSR / RSC | ✅ zero config | ⚠️ ServerStyleSheet | ✅ manual | ✅ |
|
|
29
|
+
| Hydration mismatch | ✅ tidak ada | ⚠️ hash drift | ✅ | ✅ |
|
|
30
|
+
| DevTools readable | ✅ | ❌ `sc-abc123` | ✅ | ✅ |
|
|
31
|
+
| Engine | 🦀 Rust | JS | JS | JS |
|
|
32
|
+
| TypeScript | ✅ full inference | partial | ✅ | ✅ |
|
|
36
33
|
|
|
37
34
|
---
|
|
38
35
|
|
|
@@ -40,24 +37,19 @@
|
|
|
40
37
|
|
|
41
38
|
```bash
|
|
42
39
|
npm install tailwind-styled-v4
|
|
43
|
-
|
|
44
|
-
# Setup otomatis
|
|
45
40
|
npx tw setup
|
|
46
41
|
```
|
|
47
42
|
|
|
48
|
-
`npx tw setup`
|
|
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
|
|
43
|
+
`npx tw setup` mendeteksi bundler (Next.js / Vite / Rspack), meng-inject plugin ke config, dan membuat `tailwind-styled.config.json` secara otomatis.
|
|
54
44
|
|
|
55
45
|
---
|
|
56
46
|
|
|
57
|
-
##
|
|
47
|
+
## API
|
|
58
48
|
|
|
59
49
|
### 1. Template Literal
|
|
60
50
|
|
|
51
|
+
API paling sederhana — satu tag, satu string kelas.
|
|
52
|
+
|
|
61
53
|
```tsx
|
|
62
54
|
import { tw } from "tailwind-styled-v4"
|
|
63
55
|
|
|
@@ -70,234 +62,427 @@ const Button = tw.button`
|
|
|
70
62
|
<Button onClick={handleClick}>Klik saya</Button>
|
|
71
63
|
```
|
|
72
64
|
|
|
73
|
-
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
### 2. Object Config *(direkomendasikan)*
|
|
68
|
+
|
|
69
|
+
API utama — mendukung `variants`, `states`, `sub`, `compoundVariants`, `container`, dan lebih. Semua di-resolve Rust di build time.
|
|
74
70
|
|
|
75
71
|
```tsx
|
|
76
72
|
const Button = tw.button({
|
|
77
|
-
base: "inline-flex items-center rounded-
|
|
73
|
+
base: "inline-flex items-center rounded-lg font-medium transition-all",
|
|
78
74
|
variants: {
|
|
79
75
|
intent: {
|
|
80
|
-
primary: "bg-
|
|
81
|
-
secondary: "bg-
|
|
82
|
-
|
|
83
|
-
ghost: "
|
|
76
|
+
primary: "bg-indigo-600 text-white hover:bg-indigo-700",
|
|
77
|
+
secondary: "bg-gray-200 text-gray-800 hover:bg-gray-300",
|
|
78
|
+
danger: "bg-red-600 text-white hover:bg-red-700",
|
|
79
|
+
ghost: "text-gray-600 hover:bg-gray-100",
|
|
84
80
|
},
|
|
85
81
|
size: {
|
|
86
|
-
sm: "
|
|
87
|
-
md: "
|
|
88
|
-
lg: "
|
|
82
|
+
sm: "px-3 py-1.5 text-xs",
|
|
83
|
+
md: "px-4 py-2 text-sm",
|
|
84
|
+
lg: "px-5 py-2.5 text-base",
|
|
89
85
|
},
|
|
90
86
|
},
|
|
91
87
|
defaultVariants: { intent: "primary", size: "md" },
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
88
|
+
compoundVariants: [
|
|
89
|
+
// intent=primary + size=lg → tambah shadow
|
|
90
|
+
{ intent: "primary", size: "lg", class: "shadow-md shadow-indigo-200" },
|
|
91
|
+
],
|
|
97
92
|
})
|
|
98
93
|
|
|
99
|
-
// TypeScript tahu
|
|
94
|
+
// TypeScript tahu props yang valid — autocomplete penuh
|
|
100
95
|
<Button intent="primary" size="lg">Submit</Button>
|
|
101
|
-
<Button intent="
|
|
102
|
-
<Button intent="
|
|
103
|
-
<Button loading>Memproses...</Button>
|
|
96
|
+
<Button intent="danger">Hapus</Button>
|
|
97
|
+
<Button intent="invalid" /> // ❌ Type error
|
|
104
98
|
```
|
|
105
99
|
|
|
106
|
-
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### 3. Sub-Components
|
|
103
|
+
|
|
104
|
+
Definisi slot anak langsung di config. Format `"tag:name"` untuk kontrol tag HTML — penting untuk SEO dan aksesibilitas.
|
|
107
105
|
|
|
108
106
|
```tsx
|
|
109
|
-
const Card = tw.
|
|
110
|
-
base: "rounded-
|
|
107
|
+
const Card = tw.article({
|
|
108
|
+
base: "rounded-2xl border border-gray-200 bg-white shadow-sm overflow-hidden",
|
|
111
109
|
sub: {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
// "tag:name" → render tag HTML, akses via Card.name
|
|
111
|
+
"header:header": "px-6 pt-5 pb-0 flex items-start justify-between",
|
|
112
|
+
"h2:title": "text-base font-semibold text-gray-900",
|
|
113
|
+
"section:body": "px-6 py-4 text-sm text-gray-500 leading-relaxed",
|
|
114
|
+
"footer:footer": "px-6 pb-5 pt-0 flex items-center gap-2",
|
|
115
|
+
"img:image": "w-full aspect-video object-cover",
|
|
116
|
+
// tanpa tag → render <span> (default)
|
|
117
|
+
badge: "rounded-full px-2.5 py-0.5 text-xs font-semibold bg-indigo-100 text-indigo-700",
|
|
120
118
|
},
|
|
121
119
|
})
|
|
122
120
|
|
|
123
121
|
// Penggunaan
|
|
124
|
-
<Card
|
|
125
|
-
<Card.header>
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
<Card>
|
|
123
|
+
<Card.header>
|
|
124
|
+
<Card.title>Judul Card</Card.title>
|
|
125
|
+
<Card.badge>New</Card.badge>
|
|
126
|
+
</Card.header>
|
|
127
|
+
<Card.body>Konten card di sini.</Card.body>
|
|
128
|
+
<Card.footer>
|
|
129
|
+
<Button size="sm">Detail</Button>
|
|
130
|
+
</Card.footer>
|
|
132
131
|
</Card>
|
|
133
132
|
```
|
|
134
133
|
|
|
135
|
-
|
|
134
|
+
Tag prefix di-strip otomatis dari TypeScript inference — `Card.title` bukan `Card["h2:title"]`.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### 4. `cv()` — Class Variant Function
|
|
136
139
|
|
|
137
|
-
|
|
140
|
+
Untuk styling non-komponen (className string) — berguna di utility functions, dynamic class lists, dll.
|
|
138
141
|
|
|
139
142
|
```tsx
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
143
|
+
import { cv } from "tailwind-styled-v4"
|
|
144
|
+
|
|
145
|
+
const badge = cv({
|
|
146
|
+
base: "inline-flex items-center gap-1.5 rounded-full font-medium",
|
|
147
|
+
variants: {
|
|
148
|
+
color: {
|
|
149
|
+
gray: "bg-gray-100 text-gray-700",
|
|
150
|
+
blue: "bg-blue-100 text-blue-700",
|
|
151
|
+
green: "bg-green-100 text-green-700",
|
|
152
|
+
red: "bg-red-100 text-red-700",
|
|
153
|
+
},
|
|
154
|
+
size: {
|
|
155
|
+
sm: "px-2 py-0.5 text-[10px]",
|
|
156
|
+
md: "px-2.5 py-0.5 text-xs",
|
|
157
|
+
lg: "px-3 py-1 text-sm",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
defaultVariants: { color: "gray", size: "md" },
|
|
144
161
|
})
|
|
162
|
+
|
|
163
|
+
// Returns string className, bukan komponen
|
|
164
|
+
<span className={badge({ color: "blue", size: "lg" })}>Active</span>
|
|
165
|
+
|
|
166
|
+
// Merge className tambahan
|
|
167
|
+
<span className={badge({ color: "red", className: "opacity-75" })}>Error</span>
|
|
145
168
|
```
|
|
146
169
|
|
|
147
|
-
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### 5. `states` — Boolean Props
|
|
173
|
+
|
|
174
|
+
Boolean props yang di-resolve via Rust bitmask lookup table. Tidak ada string comparison, tidak ada kondisional di render path.
|
|
148
175
|
|
|
149
176
|
```tsx
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
177
|
+
const Button = tw.button({
|
|
178
|
+
base: "inline-flex items-center px-4 py-2 rounded-lg font-medium",
|
|
179
|
+
variants: {
|
|
180
|
+
intent: { primary: "bg-indigo-600 text-white", ghost: "text-gray-600" },
|
|
181
|
+
},
|
|
182
|
+
defaultVariants: { intent: "primary" },
|
|
153
183
|
states: {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
184
|
+
loading: "opacity-60 cursor-wait pointer-events-none",
|
|
185
|
+
fullWidth: "w-full",
|
|
186
|
+
disabled: "opacity-50 cursor-not-allowed",
|
|
157
187
|
},
|
|
158
188
|
})
|
|
159
189
|
|
|
160
|
-
|
|
161
|
-
<
|
|
190
|
+
// Boolean props langsung — tidak perlu className kondisional
|
|
191
|
+
<Button loading>Memproses...</Button>
|
|
192
|
+
<Button fullWidth>Submit</Button>
|
|
193
|
+
<Button loading fullWidth>Loading full width</Button>
|
|
162
194
|
```
|
|
163
195
|
|
|
164
|
-
|
|
196
|
+
Maksimal 16 states per komponen (2¹⁶ kombinasi pre-generated di build time).
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
### 6. `state` — CSS Data-Attribute (Zero JS State)
|
|
165
201
|
|
|
166
|
-
|
|
202
|
+
Untuk toggle style tanpa React re-render — cocok untuk animasi dan transisi.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
const Dropdown = tw.div({
|
|
206
|
+
base: "overflow-hidden transition-all duration-200",
|
|
207
|
+
state: {
|
|
208
|
+
open: {
|
|
209
|
+
true: "max-h-96 opacity-100",
|
|
210
|
+
false: "max-h-0 opacity-0",
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
// Set data attribute langsung — tidak butuh setState
|
|
216
|
+
dropdownRef.current?.setAttribute("data-open", "true")
|
|
217
|
+
|
|
218
|
+
// Atau via React state
|
|
219
|
+
<Dropdown data-open={isOpen.toString()}>
|
|
220
|
+
{children}
|
|
221
|
+
</Dropdown>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### 7. `.extend()` — Inheritance
|
|
227
|
+
|
|
228
|
+
Extend komponen yang sudah ada tanpa duplikasi class.
|
|
229
|
+
|
|
230
|
+
```tsx
|
|
231
|
+
// Template literal extend
|
|
232
|
+
const PrimaryButton = Button.extend`
|
|
233
|
+
bg-indigo-600 text-white hover:bg-indigo-700
|
|
234
|
+
`
|
|
235
|
+
|
|
236
|
+
// Object config extend — tambah variant sekaligus
|
|
237
|
+
const BigDangerButton = Button.extend({
|
|
238
|
+
classes: "text-lg px-8 shadow-lg",
|
|
239
|
+
variants: { loading: { true: "animate-pulse" } },
|
|
240
|
+
defaultVariants: { intent: "danger" },
|
|
241
|
+
})
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
### 8. `container` — Container Queries
|
|
247
|
+
|
|
248
|
+
Responsive berdasarkan ukuran container parent, bukan viewport.
|
|
167
249
|
|
|
168
250
|
```tsx
|
|
169
251
|
const Card = tw.div({
|
|
170
|
-
base: "
|
|
171
|
-
|
|
172
|
-
|
|
252
|
+
base: "p-4 flex flex-col",
|
|
253
|
+
container: {
|
|
254
|
+
sm: "flex-col", // @container (min-width: 320px)
|
|
255
|
+
md: "flex-row", // @container (min-width: 640px)
|
|
256
|
+
lg: "grid-cols-3",// @container (min-width: 1024px)
|
|
173
257
|
},
|
|
258
|
+
containerName: "card", // opsional — named container
|
|
174
259
|
})
|
|
260
|
+
|
|
261
|
+
// Wrapper wajib punya @container
|
|
262
|
+
const CardWrapper = tw.div`@container`
|
|
263
|
+
|
|
264
|
+
<CardWrapper>
|
|
265
|
+
<Card>{/* responsive berdasarkan lebar CardWrapper */}</Card>
|
|
266
|
+
</CardWrapper>
|
|
175
267
|
```
|
|
176
268
|
|
|
177
|
-
|
|
269
|
+
Breakpoint default: `xs=240px`, `sm=320px`, `md=640px`, `lg=1024px`, `xl=1280px`, `2xl=1536px`.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### 9. `server.` — Server Components Only
|
|
274
|
+
|
|
275
|
+
Komponen yang di-enforce hanya boleh render di server. Dev warning otomatis jika render di browser.
|
|
178
276
|
|
|
179
277
|
```tsx
|
|
180
|
-
|
|
181
|
-
|
|
278
|
+
import { server } from "tailwind-styled-v4"
|
|
279
|
+
|
|
280
|
+
// Sama persis API-nya dengan tw — tapi compiler enforce server-only
|
|
281
|
+
const PageHeader = server.header({
|
|
282
|
+
base: "w-full border-b px-6 py-4 bg-white",
|
|
283
|
+
sub: {
|
|
284
|
+
"h1:title": "text-2xl font-bold",
|
|
285
|
+
"p:subtitle": "text-sm text-gray-500",
|
|
286
|
+
},
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const AvatarRoot = server.div({
|
|
290
|
+
base: "relative inline-flex rounded-full overflow-hidden",
|
|
182
291
|
variants: {
|
|
183
|
-
|
|
184
|
-
|
|
292
|
+
size: {
|
|
293
|
+
sm: "h-8 w-8",
|
|
294
|
+
md: "h-10 w-10",
|
|
295
|
+
lg: "h-14 w-14",
|
|
296
|
+
},
|
|
185
297
|
},
|
|
186
|
-
|
|
187
|
-
// Kalau intent=primary AND size=lg → tambah class ini
|
|
188
|
-
{ intent: "primary", size: "lg", class: "shadow-lg" },
|
|
189
|
-
],
|
|
298
|
+
defaultVariants: { size: "md" },
|
|
190
299
|
})
|
|
191
300
|
```
|
|
192
301
|
|
|
193
|
-
|
|
302
|
+
---
|
|
303
|
+
|
|
304
|
+
### 10. `createStyledSystem()` — Design System Factory
|
|
305
|
+
|
|
306
|
+
Untuk design system dengan token terpusat. Token di-inject sebagai CSS custom properties `--sys-{group}-{name}`.
|
|
194
307
|
|
|
195
308
|
```tsx
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
309
|
+
import { createStyledSystem } from "tailwind-styled-v4"
|
|
310
|
+
|
|
311
|
+
const ui = createStyledSystem({
|
|
312
|
+
tokens: {
|
|
313
|
+
colors: {
|
|
314
|
+
primary: "#6366f1",
|
|
315
|
+
danger: "#ef4444",
|
|
316
|
+
muted: "#6b7280",
|
|
317
|
+
},
|
|
318
|
+
radius: {
|
|
319
|
+
base: "0.5rem",
|
|
320
|
+
full: "9999px",
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
components: {
|
|
324
|
+
button: {
|
|
325
|
+
tag: "button",
|
|
326
|
+
base: "inline-flex items-center font-medium transition-colors",
|
|
327
|
+
variants: {
|
|
328
|
+
intent: {
|
|
329
|
+
primary: "bg-[var(--sys-colors-primary)] text-white",
|
|
330
|
+
danger: "bg-[var(--sys-colors-danger)] text-white",
|
|
331
|
+
ghost: "bg-transparent text-current hover:bg-black/5",
|
|
332
|
+
},
|
|
333
|
+
size: {
|
|
334
|
+
sm: "h-8 px-3 text-sm",
|
|
335
|
+
md: "h-10 px-4 text-base",
|
|
336
|
+
lg: "h-12 px-6 text-lg",
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
defaultVariants: { intent: "primary", size: "md" },
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
// Komponen dari sistem
|
|
345
|
+
const Button = ui.button()
|
|
346
|
+
|
|
347
|
+
// Token reference — "var(--sys-colors-primary)"
|
|
348
|
+
const primaryVar = ui.token("colors.primary")
|
|
349
|
+
|
|
350
|
+
// Update token runtime
|
|
351
|
+
ui.setTokens({ colors: { primary: "#8b5cf6" } })
|
|
205
352
|
```
|
|
206
353
|
|
|
207
354
|
---
|
|
208
355
|
|
|
209
|
-
|
|
356
|
+
### 11. `liveToken()` — Live Design Tokens
|
|
210
357
|
|
|
211
|
-
|
|
358
|
+
Token yang bisa diupdate runtime dan subscribe ke perubahannya.
|
|
212
359
|
|
|
213
|
-
```
|
|
214
|
-
|
|
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
|
-
```
|
|
360
|
+
```tsx
|
|
361
|
+
import { liveToken, tokenVar, createUseTokens } from "tailwind-styled-v4"
|
|
230
362
|
|
|
231
|
-
|
|
363
|
+
// Deklarasi token
|
|
364
|
+
const tokens = liveToken({
|
|
365
|
+
primary: "#6366f1",
|
|
366
|
+
surface: "#ffffff",
|
|
367
|
+
text: "#111827",
|
|
368
|
+
})
|
|
232
369
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
370
|
+
// CSS variable reference — dipakai di className
|
|
371
|
+
const Card = tw.div({
|
|
372
|
+
base: `
|
|
373
|
+
bg-[${tokenVar(tokens.surface)}]
|
|
374
|
+
text-[${tokenVar(tokens.text)}]
|
|
375
|
+
border-[${tokenVar(tokens.primary)}]
|
|
376
|
+
`,
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
// Hook untuk subscribe token di React
|
|
380
|
+
const useTokens = createUseTokens(tokens)
|
|
381
|
+
|
|
382
|
+
function ThemePanel() {
|
|
383
|
+
const { primary } = useTokens()
|
|
384
|
+
return <div style={{ color: primary }}>Current primary: {primary}</div>
|
|
248
385
|
}
|
|
386
|
+
|
|
387
|
+
// Update token langsung — semua subscriber re-render
|
|
388
|
+
tokens.primary.set("#8b5cf6")
|
|
249
389
|
```
|
|
250
390
|
|
|
251
|
-
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
### 12. `cn()`, `cx()`, `twMerge`
|
|
394
|
+
|
|
395
|
+
Utility untuk merge dan deduplicate Tailwind classes.
|
|
396
|
+
|
|
397
|
+
```tsx
|
|
398
|
+
import { cn, cx, twMerge } from "tailwind-styled-v4"
|
|
399
|
+
|
|
400
|
+
// cn — merge dengan dedup (alias twMerge)
|
|
401
|
+
cn("px-4 py-2", isActive && "bg-blue-500", className)
|
|
402
|
+
|
|
403
|
+
// cx — conditional class join (tanpa dedup)
|
|
404
|
+
cx("base-class", { "active-class": isActive, "disabled-class": !enabled })
|
|
405
|
+
|
|
406
|
+
// twMerge — eksplisit Tailwind conflict resolution
|
|
407
|
+
twMerge("px-4 px-8") // → "px-8" (konflik di-resolve, yang terakhir menang)
|
|
408
|
+
```
|
|
252
409
|
|
|
253
410
|
---
|
|
254
411
|
|
|
255
|
-
## Setup
|
|
412
|
+
## Setup
|
|
256
413
|
|
|
257
|
-
###
|
|
414
|
+
### Next.js
|
|
258
415
|
|
|
416
|
+
**`next.config.ts`:**
|
|
259
417
|
```ts
|
|
260
418
|
import { withTailwindStyled } from "tailwind-styled-v4/next"
|
|
261
419
|
import type { NextConfig } from "next"
|
|
262
420
|
|
|
263
421
|
const nextConfig: NextConfig = {}
|
|
264
422
|
|
|
265
|
-
export default withTailwindStyled({
|
|
423
|
+
export default withTailwindStyled({
|
|
424
|
+
// routeCss: true — generate css-manifest.json yang dibutuhkan TwCssInjector.
|
|
425
|
+
// Tanpa ini, TwCssInjector diam-diam return kosong (manifest tidak ada).
|
|
426
|
+
routeCss: true,
|
|
427
|
+
})(nextConfig)
|
|
266
428
|
```
|
|
267
429
|
|
|
268
|
-
|
|
430
|
+
**`layout.tsx`:**
|
|
431
|
+
```tsx
|
|
432
|
+
import { TwCssInjector } from "tailwind-styled-v4/runtime-css"
|
|
433
|
+
|
|
434
|
+
export default function RootLayout({ children }) {
|
|
435
|
+
return (
|
|
436
|
+
<html lang="id">
|
|
437
|
+
<head>
|
|
438
|
+
{/*
|
|
439
|
+
* TwCssInjector — opsional tapi direkomendasikan untuk production.
|
|
440
|
+
*
|
|
441
|
+
* Cara kerja:
|
|
442
|
+
* 1. withTailwindStyled({ routeCss: true }) emit css-manifest.json
|
|
443
|
+
* ke .next/static/css/tw/ saat build
|
|
444
|
+
* 2. Per request, TwCssInjector baca manifest di server dan inject CSS
|
|
445
|
+
* route-specific langsung sebagai <style> inline di HTML
|
|
446
|
+
*
|
|
447
|
+
* Tanpa TwCssInjector:
|
|
448
|
+
* CSS tetap jalan via globals.css — semua route dapat satu bundle
|
|
449
|
+
* CSS gabungan yang di-load browser via <link>.
|
|
450
|
+
*
|
|
451
|
+
* Dengan TwCssInjector:
|
|
452
|
+
* Hanya CSS yang dipakai route itu yang di-inline di HTML →
|
|
453
|
+
* tidak ada extra HTTP request, tidak ada FOUC, streaming-friendly.
|
|
454
|
+
*
|
|
455
|
+
* Kalau manifest belum ada (dev cold start), komponen ini
|
|
456
|
+
* diam-diam return kosong — tidak breaking.
|
|
457
|
+
*/}
|
|
458
|
+
<TwCssInjector />
|
|
459
|
+
</head>
|
|
460
|
+
<body>{children}</body>
|
|
461
|
+
</html>
|
|
462
|
+
)
|
|
463
|
+
}
|
|
464
|
+
```
|
|
269
465
|
|
|
466
|
+
**`globals.css`:**
|
|
270
467
|
```css
|
|
271
468
|
@import "tailwindcss";
|
|
272
|
-
@source "../.next/tw-classes/**";
|
|
273
469
|
|
|
274
470
|
:root {
|
|
275
|
-
--background: #
|
|
276
|
-
--foreground: #
|
|
471
|
+
--background: #f5f7fb;
|
|
472
|
+
--foreground: #111827;
|
|
277
473
|
}
|
|
278
474
|
|
|
279
475
|
@theme inline {
|
|
280
476
|
--color-background: var(--background);
|
|
281
477
|
--color-foreground: var(--foreground);
|
|
282
478
|
--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
479
|
}
|
|
292
480
|
```
|
|
293
481
|
|
|
294
|
-
---
|
|
295
|
-
|
|
296
|
-
## Bundler Integration
|
|
297
|
-
|
|
298
482
|
### Vite
|
|
299
483
|
|
|
300
484
|
```ts
|
|
485
|
+
// vite.config.ts
|
|
301
486
|
import { defineConfig } from "vite"
|
|
302
487
|
import react from "@vitejs/plugin-react"
|
|
303
488
|
import { tailwindStyled } from "tailwind-styled-v4/vite"
|
|
@@ -310,13 +495,12 @@ export default defineConfig({
|
|
|
310
495
|
### Rspack
|
|
311
496
|
|
|
312
497
|
```js
|
|
313
|
-
|
|
498
|
+
// rspack.config.js
|
|
314
499
|
import { tailwindStyled } from "tailwind-styled-v4/rspack"
|
|
315
500
|
|
|
316
|
-
export default
|
|
317
|
-
entry: "./src/index.ts",
|
|
501
|
+
export default {
|
|
318
502
|
plugins: [tailwindStyled()],
|
|
319
|
-
}
|
|
503
|
+
}
|
|
320
504
|
```
|
|
321
505
|
|
|
322
506
|
---
|
|
@@ -324,213 +508,149 @@ export default defineConfig({
|
|
|
324
508
|
## CLI
|
|
325
509
|
|
|
326
510
|
```bash
|
|
327
|
-
# Setup otomatis
|
|
328
|
-
npx tw setup
|
|
329
|
-
|
|
330
|
-
#
|
|
331
|
-
npx tw preflight
|
|
332
|
-
|
|
333
|
-
# Analisis workspace
|
|
334
|
-
npx tw audit
|
|
335
|
-
|
|
336
|
-
# Benchmark performa
|
|
337
|
-
npx tw benchmark
|
|
511
|
+
npx tw setup # Setup otomatis: detect bundler, patch config, pre-warm cache
|
|
512
|
+
npx tw preflight # Verifikasi setup
|
|
513
|
+
npx tw audit # Analisis workspace — unused classes, missing variants
|
|
514
|
+
npx tw benchmark # Benchmark performa scanner + compiler
|
|
338
515
|
```
|
|
339
516
|
|
|
340
517
|
---
|
|
341
518
|
|
|
342
|
-
##
|
|
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
|
|
519
|
+
## DevTools
|
|
370
520
|
|
|
371
|
-
```
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
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
|
|
521
|
+
```tsx
|
|
522
|
+
// Tambahkan ke layout untuk inspeksi komponen di browser
|
|
523
|
+
import { TwDevTools } from "tailwind-styled-v4/devtools"
|
|
524
|
+
|
|
525
|
+
// Atau pakai dynamic import untuk Next.js (ssr: false wajib)
|
|
526
|
+
import dynamic from "next/dynamic"
|
|
527
|
+
const DevTools = dynamic(
|
|
528
|
+
() => import("tailwind-styled-v4/devtools").then(m => ({ default: m.TwDevTools })),
|
|
529
|
+
{ ssr: false }
|
|
530
|
+
)
|
|
409
531
|
```
|
|
410
532
|
|
|
411
|
-
|
|
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
|
|
533
|
+
DevTools menampilkan: daftar komponen terdaftar, resolved classes per variant, state registry, container registry, dan live token values.
|
|
416
534
|
|
|
417
535
|
---
|
|
418
536
|
|
|
419
537
|
## TypeScript
|
|
420
538
|
|
|
421
|
-
|
|
539
|
+
Semua API fully typed — tidak ada `any` di public API.
|
|
422
540
|
|
|
423
541
|
```tsx
|
|
424
|
-
//
|
|
542
|
+
// Variant type inference otomatis dari config
|
|
425
543
|
const Button = tw.button({
|
|
426
544
|
variants: {
|
|
427
|
-
intent: { primary: "...", ghost: "...",
|
|
428
|
-
size:
|
|
545
|
+
intent: { primary: "...", ghost: "...", danger: "..." },
|
|
546
|
+
size: { sm: "...", md: "...", lg: "..." },
|
|
429
547
|
},
|
|
430
548
|
defaultVariants: { intent: "primary", size: "md" },
|
|
431
549
|
})
|
|
432
550
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
<Button intent="primary" /> // ✅
|
|
551
|
+
type ButtonProps = React.ComponentProps<typeof Button>
|
|
552
|
+
// → { intent?: "primary" | "ghost" | "danger", size?: "sm" | "md" | "lg", ... }
|
|
436
553
|
|
|
437
|
-
// Sub-
|
|
438
|
-
const Card = tw.
|
|
554
|
+
// Sub-component inference — tag prefix otomatis di-strip
|
|
555
|
+
const Card = tw.article({
|
|
439
556
|
sub: {
|
|
440
|
-
header:
|
|
441
|
-
"
|
|
557
|
+
"header:header": "...", // → Card.header (renders <header>)
|
|
558
|
+
"h2:title": "...", // → Card.title (renders <h2>)
|
|
559
|
+
"section:body": "...", // → Card.body (renders <section>)
|
|
560
|
+
badge: "...", // → Card.badge (renders <span>)
|
|
442
561
|
},
|
|
443
562
|
})
|
|
444
563
|
|
|
445
|
-
Card.
|
|
564
|
+
Card.header // ✅ autocomplete
|
|
565
|
+
Card.title // ✅
|
|
446
566
|
Card.xyz // ❌ TypeScript error
|
|
567
|
+
|
|
568
|
+
// .withSub<>() untuk template literal — strict mode manual
|
|
569
|
+
const Nav = tw.nav`
|
|
570
|
+
flex items-center gap-4
|
|
571
|
+
`.withSub<"logo" | "links" | "actions">()
|
|
572
|
+
|
|
573
|
+
Nav.logo // ✅
|
|
574
|
+
Nav.unknown // ❌ TypeScript error
|
|
447
575
|
```
|
|
448
576
|
|
|
449
577
|
---
|
|
450
578
|
|
|
579
|
+
## Benchmark
|
|
580
|
+
|
|
581
|
+
Diukur di Node.js 22, Rust 1.75, M1 MacBook Pro.
|
|
582
|
+
|
|
583
|
+
| Operasi | tailwind-styled-v4 | Tailwind CSS (JS) | Speedup |
|
|
584
|
+
|---|---|---|---|
|
|
585
|
+
| Scan 1000 file | **0.8 ms** | ~340 ms | **~425×** |
|
|
586
|
+
| Compile 500 class | **0.02 ms** | ~1.2 ms | **~60×** |
|
|
587
|
+
| Parse class string | **0.010 ms** | ~0.8 ms | **~80×** |
|
|
588
|
+
| Cache read/write | **0.009 ms** | ~0.5 ms | **~55×** |
|
|
589
|
+
| Watch mode rebuild | **< 5 ms** | ~85 ms | **~17×** |
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
451
593
|
## Environment Variables
|
|
452
594
|
|
|
453
595
|
| Variable | Default | Deskripsi |
|
|
454
596
|
|---|---|---|
|
|
455
597
|
| `TWS_LOG_LEVEL` | `info` | `debug\|info\|warn\|error\|silent` |
|
|
456
598
|
| `TWS_DEBUG_SCANNER` | `0` | `1` = aktifkan scanner debug logs |
|
|
457
|
-
| `
|
|
599
|
+
| `TWS_NO_NATIVE` | — | `1` = disable native module (fallback JS) |
|
|
600
|
+
| `TWS_NO_RUST` | — | `1` = disable Rust, gunakan JS fallback |
|
|
458
601
|
|
|
459
602
|
---
|
|
460
603
|
|
|
461
|
-
##
|
|
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
|
|
604
|
+
## Arsitektur
|
|
487
605
|
|
|
488
|
-
|
|
489
|
-
-
|
|
490
|
-
|
|
606
|
+
```
|
|
607
|
+
tailwind-styled-v4/
|
|
608
|
+
│
|
|
609
|
+
├── native/ # 🦀 Rust engine (NAPI-RS)
|
|
610
|
+
│ ├── src/domain/ # Core logic: variants, CSS generation, theme
|
|
611
|
+
│ ├── src/application/ # Parser, scanner, resolver, variant system
|
|
612
|
+
│ └── src/infrastructure/ # 11 NAPI bridge modules, cache backends
|
|
613
|
+
│
|
|
614
|
+
├── packages/
|
|
615
|
+
│ ├── domain/
|
|
616
|
+
│ │ ├── core/ # tw, cv, cn, cx — core API
|
|
617
|
+
│ │ ├── compiler/ # Tailwind v4 + LightningCSS pipeline
|
|
618
|
+
│ │ ├── scanner/ # File scanner (Rust-backed, ~425× faster)
|
|
619
|
+
│ │ ├── theme/ # Theme token resolution
|
|
620
|
+
│ │ ├── shared/ # Types, utilities, generated schemas
|
|
621
|
+
│ │ └── runtime-css/ # Browser-safe CSS runtime (batched inject)
|
|
622
|
+
│ │
|
|
623
|
+
│ ├── presentation/
|
|
624
|
+
│ │ ├── next/ # Next.js plugin (withTailwindStyled)
|
|
625
|
+
│ │ ├── vite/ # Vite plugin
|
|
626
|
+
│ │ └── rspack/ # Rspack plugin
|
|
627
|
+
│ │
|
|
628
|
+
│ └── infrastructure/
|
|
629
|
+
│ └── cli/ # CLI (tw setup, audit, benchmark)
|
|
630
|
+
│
|
|
631
|
+
└── examples/
|
|
632
|
+
└── next-js-app/ # Demo app: Next.js 16 + React 19
|
|
633
|
+
```
|
|
491
634
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
635
|
+
**NAPI Bridge Modules (11 modul terpisah):**
|
|
636
|
+
|
|
637
|
+
| Module | Fungsi |
|
|
638
|
+
|---|---|
|
|
639
|
+
| `napi_bridge_parsing.rs` | Class parsing (6 fungsi) |
|
|
640
|
+
| `napi_bridge_css.rs` | CSS generation (7 fungsi) |
|
|
641
|
+
| `napi_bridge_theme.rs` | Theme resolution (7 fungsi) |
|
|
642
|
+
| `napi_bridge_cache.rs` | Cache management (6 fungsi) |
|
|
643
|
+
| `napi_bridge_redis.rs` | Redis distributed cache (17 fungsi) |
|
|
644
|
+
| `napi_bridge_analysis.rs` | Performance metrics (5 fungsi) |
|
|
645
|
+
| `napi_bridge_watch.rs` | File watching (9 fungsi) |
|
|
646
|
+
| `napi_bridge_types.rs` | Type definitions |
|
|
647
|
+
| `napi_bridge_marshalling.rs` | JSON I/O |
|
|
648
|
+
| `napi_bridge_errors.rs` | Error handling |
|
|
649
|
+
| `napi_bridge.rs` | Facade (re-export semua) |
|
|
497
650
|
|
|
498
651
|
---
|
|
499
652
|
|
|
500
|
-
##
|
|
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
|
-
---
|
|
653
|
+
## Development
|
|
534
654
|
|
|
535
655
|
```bash
|
|
536
656
|
git clone https://github.com/Dictionar32/tailwind-styled-v4.git
|
|
@@ -538,39 +658,38 @@ cd tailwind-styled-v4
|
|
|
538
658
|
|
|
539
659
|
npm install
|
|
540
660
|
|
|
541
|
-
# Build Rust binary
|
|
542
|
-
npm run build
|
|
543
|
-
|
|
544
|
-
# Build Rust only
|
|
661
|
+
# Build Rust binary dulu, baru packages
|
|
545
662
|
npm run build:rust
|
|
663
|
+
npm run build:packages
|
|
664
|
+
|
|
665
|
+
# Full build
|
|
666
|
+
npm run build
|
|
546
667
|
|
|
547
668
|
# Test
|
|
548
|
-
npm run test
|
|
669
|
+
npm run test:all
|
|
549
670
|
|
|
550
|
-
# Dev mode
|
|
671
|
+
# Dev mode (watch)
|
|
551
672
|
npm run dev
|
|
552
673
|
|
|
553
674
|
# Benchmark
|
|
554
675
|
npm run bench
|
|
555
676
|
```
|
|
556
677
|
|
|
557
|
-
**Requirements:**
|
|
558
|
-
- Node.js 20+
|
|
559
|
-
- Rust 1.75+ (untuk build dari source)
|
|
678
|
+
**Requirements:** Node.js 20+, Rust 1.75+ (untuk build dari source)
|
|
560
679
|
|
|
561
680
|
---
|
|
562
681
|
|
|
563
682
|
## Contributing
|
|
564
683
|
|
|
565
|
-
PR dan issue sangat welcome
|
|
684
|
+
PR dan issue sangat welcome. Prioritas saat ini:
|
|
566
685
|
|
|
567
|
-
|
|
568
|
-
- [ ] macOS & Windows pre-built binary
|
|
686
|
+
- [ ] Pre-built binary untuk macOS arm64, x64, Linux, Windows
|
|
569
687
|
- [ ] Docs website (VitePress)
|
|
570
|
-
- [ ]
|
|
688
|
+
- [ ] Vue & Svelte adapter yang lebih matang
|
|
689
|
+
- [ ] Plugin API public docs
|
|
571
690
|
|
|
572
691
|
---
|
|
573
692
|
|
|
574
693
|
## License
|
|
575
694
|
|
|
576
|
-
[MIT](LICENSE) © Dictionar32
|
|
695
|
+
[MIT](LICENSE) © Dictionar32
|