tailwind-styled-v4 1.0.1 โ 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/animate.cjs +771 -0
- package/dist/animate.cjs.map +1 -0
- package/dist/animate.d.cts +73 -0
- package/dist/animate.d.ts +73 -0
- package/dist/animate.js +752 -0
- package/dist/animate.js.map +1 -0
- package/dist/chunk-VZEJV27B.js +11 -0
- package/dist/chunk-VZEJV27B.js.map +1 -0
- package/dist/chunk-Y5D3E72P.cjs +13 -0
- package/dist/chunk-Y5D3E72P.cjs.map +1 -0
- package/dist/css.cjs +121 -0
- package/dist/css.cjs.map +1 -0
- package/dist/css.d.cts +30 -0
- package/dist/css.d.ts +30 -0
- package/dist/css.js +112 -0
- package/dist/css.js.map +1 -0
- package/dist/devtools.cjs +1071 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +22 -0
- package/dist/devtools.d.ts +22 -0
- package/dist/devtools.js +1064 -0
- package/dist/devtools.js.map +1 -0
- package/dist/index.cjs +1353 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +655 -0
- package/dist/index.d.ts +508 -968
- package/dist/index.js +1304 -3
- package/dist/index.js.map +1 -1
- package/dist/next.cjs +248 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +54 -0
- package/dist/next.d.ts +54 -0
- package/dist/next.js +241 -0
- package/dist/next.js.map +1 -0
- package/dist/preset.cjs +423 -0
- package/dist/preset.cjs.map +1 -0
- package/dist/preset.d.cts +276 -0
- package/dist/preset.d.ts +276 -0
- package/dist/preset.js +416 -0
- package/dist/preset.js.map +1 -0
- package/dist/turbopackLoader.cjs +37 -0
- package/dist/turbopackLoader.cjs.map +1 -0
- package/dist/turbopackLoader.d.cts +12 -0
- package/dist/turbopackLoader.d.ts +12 -0
- package/dist/turbopackLoader.js +35 -0
- package/dist/turbopackLoader.js.map +1 -0
- package/dist/vite.cjs +138 -0
- package/dist/vite.cjs.map +1 -0
- package/dist/vite.d.cts +51 -0
- package/dist/vite.d.ts +51 -0
- package/dist/vite.js +128 -0
- package/dist/vite.js.map +1 -0
- package/dist/webpackLoader.cjs +51 -0
- package/dist/webpackLoader.cjs.map +1 -0
- package/dist/webpackLoader.d.cts +17 -0
- package/dist/webpackLoader.d.ts +17 -0
- package/dist/webpackLoader.js +49 -0
- package/dist/webpackLoader.js.map +1 -0
- package/package.json +65 -32
- package/CHANGELOG.md +0 -75
- package/LICENSE +0 -21
- package/README.md +0 -608
- package/dist/cli/init.js +0 -208
- package/dist/compiler/index.d.mts +0 -214
- package/dist/compiler/index.d.ts +0 -214
- package/dist/compiler/index.js +0 -546
- package/dist/compiler/index.js.map +0 -1
- package/dist/compiler/index.mjs +0 -504
- package/dist/compiler/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -1115
- package/dist/index.mjs +0 -4
- package/dist/index.mjs.map +0 -1
- package/dist/turbopack-loader.js +0 -232
- package/dist/webpack-loader.js +0 -213
package/README.md
DELETED
|
@@ -1,608 +0,0 @@
|
|
|
1
|
-
# ๐จ tailwind-styled-v4
|
|
2
|
-
|
|
3
|
-
> **styled-components meets Tailwind CSS** โ dengan type safety penuh, zero DOM pollution, dan zero-runtime build output.
|
|
4
|
-
|
|
5
|
-
```tsx
|
|
6
|
-
const Button = tw.button({
|
|
7
|
-
base: "px-4 py-2 rounded-lg font-medium transition",
|
|
8
|
-
variants: {
|
|
9
|
-
variant: {
|
|
10
|
-
primary: "bg-blue-500 text-white hover:bg-blue-600",
|
|
11
|
-
ghost: "bg-transparent border hover:bg-zinc-800",
|
|
12
|
-
},
|
|
13
|
-
size: { sm: "h-8 text-sm", lg: "h-12 text-base" },
|
|
14
|
-
},
|
|
15
|
-
defaultVariants: { variant: "primary", size: "sm" },
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
<Button variant="primary" size="lg" p={4} rounded="card" />
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## โจ Fitur Utama
|
|
24
|
-
|
|
25
|
-
| Fitur | Keterangan |
|
|
26
|
-
|-------|------------|
|
|
27
|
-
| ๐งฑ **styled-components API** | Familiar, chainable, SSR-safe |
|
|
28
|
-
| ๐ญ **Variant System** | `variants`, `compoundVariants`, `defaultVariants` |
|
|
29
|
-
| ๐ **Responsive Props** | `p={{ sm: 2, lg: 8 }}` โ responsive object syntax |
|
|
30
|
-
| ๐จ **Design Tokens** | `bg="primary"` โ `bg-blue-600` via theme store |
|
|
31
|
-
| โก **Zero-Runtime** | Build-time transform โ static `className` strings |
|
|
32
|
-
| ๐ **Plugin System** | Register custom prop resolvers |
|
|
33
|
-
| ๐ก๏ธ **Type Safe** | Full TypeScript generics & inference |
|
|
34
|
-
| ๐ซ **Zero DOM Pollution** | Props tidak bocor ke HTML attributes |
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## ๐ฆ Instalasi
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm install tailwind-styled-v4 styled-components tailwind-merge
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## ๐ Quick Start
|
|
47
|
-
|
|
48
|
-
### 1๏ธโฃ Template Literal (paling simpel)
|
|
49
|
-
|
|
50
|
-
```tsx
|
|
51
|
-
import { tw } from "tailwind-styled-v4"
|
|
52
|
-
|
|
53
|
-
const Box = tw.div`p-4 bg-zinc-900 rounded-xl`
|
|
54
|
-
const Title = tw.h1`text-2xl font-bold text-white`
|
|
55
|
-
const Badge = tw.span`px-2 py-1 rounded-full text-xs bg-blue-500 text-white`
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### 2๏ธโฃ Object Config + Variants
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
const Button = tw.button({
|
|
62
|
-
base: "inline-flex items-center font-medium transition rounded-lg",
|
|
63
|
-
variants: {
|
|
64
|
-
variant: {
|
|
65
|
-
primary: "bg-blue-500 text-white hover:bg-blue-600",
|
|
66
|
-
ghost: "bg-transparent border border-zinc-700 hover:bg-zinc-800",
|
|
67
|
-
danger: "bg-red-500 text-white hover:bg-red-600",
|
|
68
|
-
},
|
|
69
|
-
size: {
|
|
70
|
-
sm: "h-8 px-3 text-sm",
|
|
71
|
-
md: "h-10 px-4 text-sm",
|
|
72
|
-
lg: "h-12 px-6 text-base",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
compoundVariants: [
|
|
76
|
-
{ variant: "primary", size: "lg", class: "shadow-lg shadow-blue-500/30" },
|
|
77
|
-
],
|
|
78
|
-
defaultVariants: { variant: "primary", size: "md" },
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// Usage
|
|
82
|
-
<Button variant="ghost" size="lg" />
|
|
83
|
-
<Button variant="danger" />
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### 3๏ธโฃ Prop Shorthand (responsive-ready)
|
|
87
|
-
|
|
88
|
-
```tsx
|
|
89
|
-
// Scalar
|
|
90
|
-
<Box p={4} bg="zinc-900" rounded="xl" flex />
|
|
91
|
-
|
|
92
|
-
// Responsive object
|
|
93
|
-
<Box p={{ base: 2, md: 4, lg: 8 }} cols={{ sm: 1, md: 2, lg: 3 }} />
|
|
94
|
-
|
|
95
|
-
// Design token
|
|
96
|
-
<Box bg="primary" text="foreground" rounded="card" shadow="modal" />
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### 4๏ธโฃ Extend Component
|
|
100
|
-
|
|
101
|
-
```tsx
|
|
102
|
-
const Card = tw.div`p-6 rounded-2xl bg-zinc-900`
|
|
103
|
-
const HeroCard = Card.extend`shadow-2xl ring-1 ring-zinc-700`
|
|
104
|
-
const DarkCard = Card.withVariants({
|
|
105
|
-
variants: { elevated: { true: "shadow-2xl", false: "" } }
|
|
106
|
-
})
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### 5๏ธโฃ Wrap Komponen Apapun
|
|
110
|
-
|
|
111
|
-
```tsx
|
|
112
|
-
// shadcn/ui, Radix, komponen custom
|
|
113
|
-
const TwButton = withTw(Button, "px-4 py-2")
|
|
114
|
-
const TwDialog = tw(Dialog.Content)`p-6 rounded-2xl`
|
|
115
|
-
|
|
116
|
-
<TwButton p={4} rounded="xl" bg="zinc-900" />
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
---
|
|
120
|
-
|
|
121
|
-
## ๐ญ Variant System
|
|
122
|
-
|
|
123
|
-
### Basic Variants
|
|
124
|
-
|
|
125
|
-
```tsx
|
|
126
|
-
const Alert = tw.div({
|
|
127
|
-
base: "p-4 rounded-lg border",
|
|
128
|
-
variants: {
|
|
129
|
-
status: {
|
|
130
|
-
info: "bg-sky-50 border-sky-200 text-sky-800",
|
|
131
|
-
success: "bg-emerald-50 border-emerald-200 text-emerald-800",
|
|
132
|
-
warning: "bg-amber-50 border-amber-200 text-amber-800",
|
|
133
|
-
danger: "bg-red-50 border-red-200 text-red-800",
|
|
134
|
-
},
|
|
135
|
-
},
|
|
136
|
-
defaultVariants: { status: "info" },
|
|
137
|
-
})
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
### Compound Variants
|
|
141
|
-
|
|
142
|
-
```tsx
|
|
143
|
-
// Class tambahan ketika 2+ variant aktif bersamaan
|
|
144
|
-
compoundVariants: [
|
|
145
|
-
{ variant: "primary", size: "lg", class: "shadow-lg shadow-blue-500/30" },
|
|
146
|
-
{ variant: "ghost", size: "sm", class: "opacity-70" },
|
|
147
|
-
]
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### withVariants โ tambah variant ke komponen existing
|
|
151
|
-
|
|
152
|
-
```tsx
|
|
153
|
-
const EnhancedButton = Button.withVariants({
|
|
154
|
-
variants: {
|
|
155
|
-
loading: { true: "opacity-50 cursor-not-allowed", false: "" }
|
|
156
|
-
}
|
|
157
|
-
})
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
---
|
|
161
|
-
|
|
162
|
-
## ๐ Prop Engine
|
|
163
|
-
|
|
164
|
-
Semua prop otomatis di-convert ke Tailwind class. Tidak perlu `className="..."`.
|
|
165
|
-
|
|
166
|
-
### Spacing
|
|
167
|
-
```tsx
|
|
168
|
-
<Box p={4} px={6} py={2} m="auto" mx={4} gap={3} />
|
|
169
|
-
// โ p-4 px-6 py-2 m-auto mx-4 gap-3
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Flexbox & Grid
|
|
173
|
-
```tsx
|
|
174
|
-
<Box flex items="center" justify="between" wrap />
|
|
175
|
-
<Box cols={3} rows={2} colSpan={2} gap={4} />
|
|
176
|
-
// โ flex items-center justify-between flex-wrap
|
|
177
|
-
// โ grid-cols-3 grid-rows-2 col-span-2 gap-4
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Colors & Typography
|
|
181
|
-
```tsx
|
|
182
|
-
<Box bg="zinc-900" text="white" border ring="blue-500" />
|
|
183
|
-
<Box fontSize="heading" font="sans" leading="relaxed" tracking="tight" />
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Layout & Position
|
|
187
|
-
```tsx
|
|
188
|
-
<Box pos="absolute" top={0} right={0} z={50} inset="x-4" />
|
|
189
|
-
<Box overflow="hidden" rounded="xl" shadow="card" opacity={90} />
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### Responsive Object
|
|
193
|
-
```tsx
|
|
194
|
-
// Semua prop mendukung responsive object
|
|
195
|
-
<Box
|
|
196
|
-
p={{ base: 2, sm: 4, md: 6, lg: 8 }}
|
|
197
|
-
cols={{ base: 1, md: 2, lg: 3 }}
|
|
198
|
-
hidden={{ base: true, md: false }}
|
|
199
|
-
/>
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
---
|
|
203
|
-
|
|
204
|
-
## ๐จ Theme & Design Tokens
|
|
205
|
-
|
|
206
|
-
### Setup
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
// app/layout.tsx
|
|
210
|
-
import { loadDefaultTheme } from "tailwind-styled-v4"
|
|
211
|
-
loadDefaultTheme()
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Custom Theme
|
|
215
|
-
|
|
216
|
-
```tsx
|
|
217
|
-
import { setTheme } from "tailwind-styled-v4"
|
|
218
|
-
|
|
219
|
-
setTheme({
|
|
220
|
-
colors: {
|
|
221
|
-
primary: "blue-600",
|
|
222
|
-
secondary: "violet-600",
|
|
223
|
-
brand: "cyan-400",
|
|
224
|
-
background: "zinc-950",
|
|
225
|
-
surface: "zinc-900",
|
|
226
|
-
muted: "zinc-500",
|
|
227
|
-
},
|
|
228
|
-
radius: {
|
|
229
|
-
card: "2xl",
|
|
230
|
-
button: "lg",
|
|
231
|
-
badge: "full",
|
|
232
|
-
input: "md",
|
|
233
|
-
},
|
|
234
|
-
spacing: {
|
|
235
|
-
section: "16",
|
|
236
|
-
page: "24",
|
|
237
|
-
},
|
|
238
|
-
duration: {
|
|
239
|
-
fast: "150",
|
|
240
|
-
normal: "200",
|
|
241
|
-
slow: "300",
|
|
242
|
-
},
|
|
243
|
-
})
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
### Penggunaan Token
|
|
247
|
-
|
|
248
|
-
```tsx
|
|
249
|
-
// Token otomatis di-resolve
|
|
250
|
-
<Box bg="primary" /> // โ bg-blue-600
|
|
251
|
-
<Box rounded="card" /> // โ rounded-2xl
|
|
252
|
-
<Box p="section" /> // โ p-16
|
|
253
|
-
<Box duration="fast" /> // โ duration-150
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
---
|
|
257
|
-
|
|
258
|
-
## ๐ฏ cv() โ Standalone Class Variants
|
|
259
|
-
|
|
260
|
-
Cocok untuk **shadcn/ui**, **Radix UI**, atau komponen headless apapun tanpa styled-components.
|
|
261
|
-
|
|
262
|
-
```tsx
|
|
263
|
-
import { cv } from "tailwind-styled-v4"
|
|
264
|
-
|
|
265
|
-
const buttonVariants = cv({
|
|
266
|
-
base: "inline-flex items-center rounded-lg font-medium transition",
|
|
267
|
-
variants: {
|
|
268
|
-
variant: {
|
|
269
|
-
primary: "bg-blue-500 text-white hover:bg-blue-600",
|
|
270
|
-
ghost: "bg-transparent border hover:bg-zinc-800",
|
|
271
|
-
},
|
|
272
|
-
size: {
|
|
273
|
-
sm: "h-8 px-3 text-sm",
|
|
274
|
-
md: "h-10 px-4 text-sm",
|
|
275
|
-
lg: "h-12 px-6 text-base",
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
compoundVariants: [
|
|
279
|
-
{ variant: "primary", size: "lg", class: "shadow-lg" }
|
|
280
|
-
],
|
|
281
|
-
defaultVariants: { variant: "primary", size: "md" },
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
// Pakai di komponen apapun
|
|
285
|
-
function Button({ variant, size, className, ...props }) {
|
|
286
|
-
return <button className={buttonVariants({ variant, size, className })} {...props} />
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
---
|
|
291
|
-
|
|
292
|
-
## ๐ Plugin System
|
|
293
|
-
|
|
294
|
-
### Built-in Plugins
|
|
295
|
-
|
|
296
|
-
```tsx
|
|
297
|
-
import { loadBuiltinPlugins } from "tailwind-styled-v4"
|
|
298
|
-
loadBuiltinPlugins()
|
|
299
|
-
|
|
300
|
-
// Setelah load, props berikut tersedia:
|
|
301
|
-
<Box motionSafe backdropBlur="md" gradientFrom="blue-500" gradientTo="purple-500" />
|
|
302
|
-
<Box scrollSmooth printHidden autoRows="fr" />
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### Tailwind v4 Plugins
|
|
306
|
-
|
|
307
|
-
```tsx
|
|
308
|
-
import { loadAllV4Plugins } from "tailwind-styled-v4"
|
|
309
|
-
loadAllV4Plugins()
|
|
310
|
-
|
|
311
|
-
// Typography
|
|
312
|
-
<article prose="lg" proseDark proseMaxW={false} />
|
|
313
|
-
// โ prose prose-lg dark:prose-invert max-w-none
|
|
314
|
-
|
|
315
|
-
// Forms
|
|
316
|
-
<input formInput />
|
|
317
|
-
<select formSelect />
|
|
318
|
-
|
|
319
|
-
// Aspect Ratio
|
|
320
|
-
<div aspectRatio="video" /> // โ aspect-video
|
|
321
|
-
<div aspectRatio="square" /> // โ aspect-square
|
|
322
|
-
|
|
323
|
-
// Animations
|
|
324
|
-
<div enterAnim="fade-up" /> // โ animate-in fade-in slide-in-from-bottom-4
|
|
325
|
-
<div exitAnim="fade" /> // โ animate-out fade-out
|
|
326
|
-
|
|
327
|
-
// Interactivity
|
|
328
|
-
<button hoverBg="zinc-800" hoverScale focusRing="blue-500" activeScale />
|
|
329
|
-
|
|
330
|
-
// Container Queries
|
|
331
|
-
<div cqContainer /> // โ @container
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Custom Plugin
|
|
335
|
-
|
|
336
|
-
```tsx
|
|
337
|
-
import { registerPlugin } from "tailwind-styled-v4"
|
|
338
|
-
|
|
339
|
-
registerPlugin({
|
|
340
|
-
name: "glass",
|
|
341
|
-
props: {
|
|
342
|
-
glass: (v) => v === true
|
|
343
|
-
? "backdrop-blur-md bg-white/10 border border-white/20"
|
|
344
|
-
: `backdrop-blur-${v} bg-white/10`,
|
|
345
|
-
},
|
|
346
|
-
})
|
|
347
|
-
|
|
348
|
-
// Langsung bisa dipakai
|
|
349
|
-
<Box glass />
|
|
350
|
-
<Box glass="xl" />
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
---
|
|
354
|
-
|
|
355
|
-
## โก Zero-Runtime Mode
|
|
356
|
-
|
|
357
|
-
### Next.js
|
|
358
|
-
|
|
359
|
-
```ts
|
|
360
|
-
// next.config.ts
|
|
361
|
-
import { withTailwindStyled } from "tailwind-styled-v4/compiler"
|
|
362
|
-
|
|
363
|
-
export default withTailwindStyled({
|
|
364
|
-
mode: "zero-runtime", // transform tw.div`...` โ static className
|
|
365
|
-
addDataAttr: true, // tambah data-tw="hash" untuk DevTools
|
|
366
|
-
})(nextConfig)
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
### Vite
|
|
370
|
-
|
|
371
|
-
```ts
|
|
372
|
-
// vite.config.ts
|
|
373
|
-
import { tailwindStyledPlugin } from "tailwind-styled-v4/compiler"
|
|
374
|
-
|
|
375
|
-
export default defineConfig({
|
|
376
|
-
plugins: [
|
|
377
|
-
react(),
|
|
378
|
-
tailwindStyledPlugin({ mode: "zero-runtime" }),
|
|
379
|
-
],
|
|
380
|
-
})
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
**Before:**
|
|
384
|
-
```tsx
|
|
385
|
-
const Box = tw.div`p-4 bg-zinc-900 rounded-xl`
|
|
386
|
-
```
|
|
387
|
-
|
|
388
|
-
**After (build output):**
|
|
389
|
-
```tsx
|
|
390
|
-
const Box = styled.div.attrs(() => ({ className: "p-4 bg-zinc-900 rounded-xl" }))``
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
> ๐ Nol JS runtime cost โ `className` jadi static string, bukan computed di setiap render.
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## ๐ก๏ธ shouldForwardProp
|
|
398
|
-
|
|
399
|
-
Semua tw props **otomatis diblokir** dari forwarding ke DOM. Tidak ada warning `Unknown prop` di console.
|
|
400
|
-
|
|
401
|
-
```tsx
|
|
402
|
-
// โ
Tidak ada warning di DOM
|
|
403
|
-
<Box p={4} flex cols={3} bg="zinc-900" />
|
|
404
|
-
|
|
405
|
-
// Transient props ($) selalu di-forward
|
|
406
|
-
<Box $isActive={true} />
|
|
407
|
-
|
|
408
|
-
// Manual block/allow
|
|
409
|
-
import { blockProp, allowProp } from "tailwind-styled-v4"
|
|
410
|
-
blockProp("customProp")
|
|
411
|
-
allowProp("size") // kalau mau size sampai ke DOM
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
---
|
|
415
|
-
|
|
416
|
-
## ๐งช Validation (Dev Only)
|
|
417
|
-
|
|
418
|
-
```tsx
|
|
419
|
-
import { validateVariantProps, buildSchema } from "tailwind-styled-v4"
|
|
420
|
-
|
|
421
|
-
const schema = buildSchema("button", {
|
|
422
|
-
variant: { primary: "...", ghost: "..." },
|
|
423
|
-
size: { sm: "...", lg: "..." },
|
|
424
|
-
})
|
|
425
|
-
|
|
426
|
-
validateVariantProps({ variant: "invalid" }, schema)
|
|
427
|
-
// [tw-error] Invalid prop "variant"="invalid" on <button>
|
|
428
|
-
// Expected one of: "primary" | "ghost"
|
|
429
|
-
// Got: "invalid"
|
|
430
|
-
// Hint: Add "invalid" to the variants config, or fix the typo.
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
> โ ๏ธ Validation hanya aktif di `NODE_ENV !== "production"` โ **zero overhead di production**.
|
|
434
|
-
|
|
435
|
-
---
|
|
436
|
-
|
|
437
|
-
## ๐ง Utils
|
|
438
|
-
|
|
439
|
-
### cx / cxm
|
|
440
|
-
|
|
441
|
-
```tsx
|
|
442
|
-
import { cx, cxm } from "tailwind-styled-v4"
|
|
443
|
-
|
|
444
|
-
// cx โ lightweight class joiner
|
|
445
|
-
cx("p-4", isActive && "bg-blue-500", ["rounded", "shadow"])
|
|
446
|
-
cx({ "p-4": true, "hidden": false })
|
|
447
|
-
|
|
448
|
-
// cxm โ cx + tailwind-merge (conflict resolution)
|
|
449
|
-
cxm("p-2", "p-4") // โ "p-4" (p-2 dihapus)
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
### Class Parser
|
|
453
|
-
|
|
454
|
-
```tsx
|
|
455
|
-
import { parseClassString, splitModifiers, groupByModifier } from "tailwind-styled-v4"
|
|
456
|
-
|
|
457
|
-
splitModifiers("p-4 hover:bg-blue-500 md:p-8")
|
|
458
|
-
// โ { base: ["p-4"], modifiers: ["hover:bg-blue-500", "md:p-8"] }
|
|
459
|
-
|
|
460
|
-
groupByModifier("p-4 hover:bg-blue-500 hover:text-white")
|
|
461
|
-
// โ { "": ["p-4"], hover: ["bg-blue-500", "text-white"] }
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
### Safelist Generator
|
|
465
|
-
|
|
466
|
-
```tsx
|
|
467
|
-
import { generateSafelist } from "tailwind-styled-v4/compiler"
|
|
468
|
-
|
|
469
|
-
generateSafelist({
|
|
470
|
-
cwd: process.cwd(),
|
|
471
|
-
scanDirs: ["src", "app", "components"],
|
|
472
|
-
outputPath: "tailwind.safelist.json",
|
|
473
|
-
})
|
|
474
|
-
// โ Safelist: 247 classes โ /project/tailwind.safelist.json
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
---
|
|
478
|
-
|
|
479
|
-
## ๐ Struktur Engine
|
|
480
|
-
|
|
481
|
-
```
|
|
482
|
-
src/
|
|
483
|
-
โโโ core/
|
|
484
|
-
โ โโโ tw.ts ๐ฏ Proxy-free tw object (v2 fix)
|
|
485
|
-
โ โโโ createComponent.ts ๐๏ธ Single styled-chain factory
|
|
486
|
-
โ โโโ templateParser.ts ๐ Tagged template literal parser
|
|
487
|
-
โ โโโ cv.ts ๐ญ Standalone class variants
|
|
488
|
-
โ โโโ withTw.ts ๐ HOC wrapper untuk komponen apapun
|
|
489
|
-
โ โโโ styledFactory.ts ๐ญ Thin styled-components wrapper
|
|
490
|
-
โ
|
|
491
|
-
โโโ runtime/
|
|
492
|
-
โ โโโ propEngine.ts โ๏ธ 80+ prop โ class resolvers
|
|
493
|
-
โ โโโ variantEngine.ts ๐ญ Variant resolution
|
|
494
|
-
โ โโโ compoundVariant.ts ๐ Compound variant resolution
|
|
495
|
-
โ โโโ responsiveEngine.ts๐ Responsive object handler
|
|
496
|
-
โ โโโ classResolver.ts ๐ Full pipeline orchestrator
|
|
497
|
-
โ
|
|
498
|
-
โโโ theme/
|
|
499
|
-
โ โโโ themeStore.ts ๐จ Global token store
|
|
500
|
-
โ โโโ tokenResolver.ts ๐ Token โ class segment resolver
|
|
501
|
-
โ โโโ defaultTheme.ts ๐ฆ Ready-to-use semantic tokens
|
|
502
|
-
โ
|
|
503
|
-
โโโ plugins/
|
|
504
|
-
โ โโโ pluginEngine.ts ๐ Plugin registry & resolver
|
|
505
|
-
โ โโโ registerPlugin.ts ๐ Public plugin registration API
|
|
506
|
-
โ โโโ builtinPlugins.ts ๐งฐ Animation, grid, backdrop, gradient
|
|
507
|
-
โ โโโ tailwindV4Plugins.ts ๐ Typography, forms, aspect, animations
|
|
508
|
-
โ
|
|
509
|
-
โโโ compiler/
|
|
510
|
-
โ โโโ astTransform.ts ๐ Source code transformer (zero-runtime)
|
|
511
|
-
โ โโโ transformTw.ts ๐ ๏ธ Transform orchestrator
|
|
512
|
-
โ โโโ vitePlugin.ts โก Vite/Rollup plugin
|
|
513
|
-
โ โโโ swcPlugin.ts ๐ง Next.js SWC transform
|
|
514
|
-
โ โโโ webpackLoader.ts ๐ฆ Webpack loader
|
|
515
|
-
โ โโโ classExtractor.ts ๐ Class extraction for safelist
|
|
516
|
-
โ
|
|
517
|
-
โโโ validation/
|
|
518
|
-
โ โโโ propValidator.ts ๐ก๏ธ Dev-only prop validation
|
|
519
|
-
โ
|
|
520
|
-
โโโ utils/
|
|
521
|
-
โโโ cx.ts ๐ Lightweight class joiner
|
|
522
|
-
โโโ classParser.ts ๐ค Class string utilities
|
|
523
|
-
โโโ hash.ts #๏ธโฃ Stable hash generator
|
|
524
|
-
โโโ merge.ts ๐ twMerge wrapper
|
|
525
|
-
โโโ isObject.ts ๐ Type guard utility
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## ๐งช Test Coverage
|
|
531
|
-
|
|
532
|
-
```
|
|
533
|
-
228 tests โ 228 pass โ
|
|
534
|
-
|
|
535
|
-
๐ต templateParser 9/9
|
|
536
|
-
๐ต themeStore 7/7
|
|
537
|
-
๐ต tokenResolver 7/7
|
|
538
|
-
๐ต defaultTheme 2/2
|
|
539
|
-
๐ต responsiveEngine 7/7
|
|
540
|
-
๐ต variantEngine 5/5
|
|
541
|
-
๐ต compoundVariant 5/5
|
|
542
|
-
๐ต propEngine 15/15
|
|
543
|
-
๐ต classResolver 2/2
|
|
544
|
-
๐ต pluginEngine 4/4
|
|
545
|
-
๐ต registerPlugin 3/3
|
|
546
|
-
๐ต builtinPlugins 7/7
|
|
547
|
-
๐ต tailwindV4Plugins 14/14
|
|
548
|
-
๐ต cv 4/4
|
|
549
|
-
๐ต propValidator 6/6
|
|
550
|
-
๐ต astTransform 10/10
|
|
551
|
-
๐ต utils 9/9
|
|
552
|
-
๐ต withTw 5/5
|
|
553
|
-
๐ต styledFactory 6/6
|
|
554
|
-
๐ต classParser 10/10
|
|
555
|
-
๐ต cx / cxm 8/8
|
|
556
|
-
๐ต classExtractor 4/4
|
|
557
|
-
๐ต generateSafelist 4/4
|
|
558
|
-
๐ต barrel exports 67/67
|
|
559
|
-
๐ต end-to-end 5/5
|
|
560
|
-
```
|
|
561
|
-
|
|
562
|
-
---
|
|
563
|
-
|
|
564
|
-
## ๐ Changelog v2
|
|
565
|
-
|
|
566
|
-
### ๐ด Bug Fixes
|
|
567
|
-
|
|
568
|
-
**`tw` undefined setelah bundle split (Vite/tsup)**
|
|
569
|
-
```
|
|
570
|
-
BEFORE: export const tw = new Proxy(fn, { get })
|
|
571
|
-
โ Proxy + tsup splitting = tw undefined di Next.js/Vite
|
|
572
|
-
|
|
573
|
-
AFTER: Object.assign(twCallable, { div, button, ... })
|
|
574
|
-
โ Plain object, transparan ke semua bundler
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
**Double wrap SSR hydration mismatch**
|
|
578
|
-
```
|
|
579
|
-
BEFORE: styled(tag)`` โ styled(BaseStyled).attrs()``
|
|
580
|
-
โ Double wrap = SSR hydration mismatch + SC v6 error
|
|
581
|
-
|
|
582
|
-
AFTER: styled(tag).withConfig().attrs()``
|
|
583
|
-
โ Single chain, satu SC instance per component
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
**shouldForwardProp hardcoded**
|
|
587
|
-
```
|
|
588
|
-
BEFORE: Hanya blokir variant names yang hardcoded
|
|
589
|
-
โ User variant keys bocor ke DOM
|
|
590
|
-
|
|
591
|
-
AFTER: Built dynamically dari variant keys aktual
|
|
592
|
-
โ Zero DOM pollution apapun nama variant-nya
|
|
593
|
-
```
|
|
594
|
-
|
|
595
|
-
**TypeScript generic inference**
|
|
596
|
-
```
|
|
597
|
-
BEFORE: TwTagFactory tidak generic
|
|
598
|
-
โ "Binding element '$isHover' implicitly has 'any' type"
|
|
599
|
-
|
|
600
|
-
AFTER: TwTagFactory<P> generic function
|
|
601
|
-
โ Full type inference di template interpolations
|
|
602
|
-
```
|
|
603
|
-
|
|
604
|
-
---
|
|
605
|
-
|
|
606
|
-
## ๐ License
|
|
607
|
-
|
|
608
|
-
MIT ยฉ tailwind-styled-v4
|