tailwind-styled-v4 1.0.0 โ†’ 1.0.1

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 CHANGED
@@ -1,50 +1,70 @@
1
- # tailwind-styled-v4
1
+ # ๐ŸŽจ tailwind-styled-v4
2
2
 
3
- **PRO Engine** โ€” styled-components + Tailwind CSS v4, zero-runtime compile option, variant system, responsive props, plugin ecosystem.
3
+ > **styled-components meets Tailwind CSS** โ€” dengan type safety penuh, zero DOM pollution, dan zero-runtime build output.
4
4
 
5
- > Melampaui `twin.macro` dengan tetap kompatibel penuh terhadap Next.js 16 ยท React 19 ยท Tailwind CSS v4 ยท styled-components v6 ยท TypeScript 5 ยท shadcn/ui.
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 |
6
35
 
7
36
  ---
8
37
 
9
- ## Installation
38
+ ## ๐Ÿ“ฆ Instalasi
10
39
 
11
40
  ```bash
12
- npm install tailwind-styled-v4
13
- # peerDependencies
14
- npm install styled-components tailwind-merge clsx
41
+ npm install tailwind-styled-v4 styled-components tailwind-merge
15
42
  ```
16
43
 
17
44
  ---
18
45
 
19
- ## Quick Start
46
+ ## ๐Ÿš€ Quick Start
20
47
 
21
- ### Template Literal (sederhana)
48
+ ### 1๏ธโƒฃ Template Literal (paling simpel)
22
49
 
23
50
  ```tsx
24
51
  import { tw } from "tailwind-styled-v4"
25
52
 
26
- const Card = tw.div`
27
- p-6
28
- bg-zinc-900
29
- rounded-2xl
30
- border border-zinc-800
31
- `
32
-
33
- // Responsive props inline
34
- <Card p={{ base: 4, md: 6, lg: 8 }} />
35
- // โ†’ p-4 md:p-6 lg:p-8
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`
36
56
  ```
37
57
 
38
- ### Object Config (full power)
58
+ ### 2๏ธโƒฃ Object Config + Variants
39
59
 
40
60
  ```tsx
41
61
  const Button = tw.button({
42
- base: "inline-flex items-center justify-center rounded-lg font-medium transition",
62
+ base: "inline-flex items-center font-medium transition rounded-lg",
43
63
  variants: {
44
64
  variant: {
45
- primary: "bg-blue-600 text-white hover:bg-blue-700",
65
+ primary: "bg-blue-500 text-white hover:bg-blue-600",
46
66
  ghost: "bg-transparent border border-zinc-700 hover:bg-zinc-800",
47
- danger: "bg-red-600 text-white hover:bg-red-700",
67
+ danger: "bg-red-500 text-white hover:bg-red-600",
48
68
  },
49
69
  size: {
50
70
  sm: "h-8 px-3 text-sm",
@@ -53,278 +73,536 @@ const Button = tw.button({
53
73
  },
54
74
  },
55
75
  compoundVariants: [
56
- { variant: "primary", size: "lg", class: "shadow-lg shadow-blue-600/30" },
76
+ { variant: "primary", size: "lg", class: "shadow-lg shadow-blue-500/30" },
57
77
  ],
58
78
  defaultVariants: { variant: "primary", size: "md" },
59
79
  })
60
80
 
61
81
  // Usage
62
- <Button variant="primary" size="lg">Click me</Button>
63
- <Button variant="ghost" size="sm">Cancel</Button>
82
+ <Button variant="ghost" size="lg" />
83
+ <Button variant="danger" />
64
84
  ```
65
85
 
66
- ### Extend existing component
86
+ ### 3๏ธโƒฃ Prop Shorthand (responsive-ready)
67
87
 
68
88
  ```tsx
69
- const HeroCard = Card.extend`shadow-2xl ring-1 ring-zinc-700/50`
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" />
70
97
  ```
71
98
 
72
- ### Wrap any component (shadcn/ui, Radix, dll)
99
+ ### 4๏ธโƒฃ Extend Component
73
100
 
74
101
  ```tsx
75
- import { withTw, tw } from "tailwind-styled-v4"
76
- import { Button } from "@/components/ui/button" // shadcn
77
- import Link from "next/link"
78
-
79
- const TwButton = withTw(Button, "px-4 py-2")
80
- const NavLink = tw(Link)`text-zinc-400 hover:text-zinc-100 transition-colors`
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
+ })
81
107
  ```
82
108
 
83
- ### Standalone class variants (headless/shadcn compatible)
109
+ ### 5๏ธโƒฃ Wrap Komponen Apapun
84
110
 
85
111
  ```tsx
86
- import { cv } from "tailwind-styled-v4"
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
87
122
 
88
- const badgeCv = cv({
89
- base: "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
123
+ ### Basic Variants
124
+
125
+ ```tsx
126
+ const Alert = tw.div({
127
+ base: "p-4 rounded-lg border",
90
128
  variants: {
91
- color: {
92
- blue: "bg-blue-100 text-blue-800",
93
- green: "bg-green-100 text-green-800",
94
- red: "bg-red-100 text-red-800",
95
- }
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
+ },
96
135
  },
97
- defaultVariants: { color: "blue" }
136
+ defaultVariants: { status: "info" },
98
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
99
151
 
100
- // Use anywhere
101
- <span className={badgeCv({ color: "green" })}>Active</span>
152
+ ```tsx
153
+ const EnhancedButton = Button.withVariants({
154
+ variants: {
155
+ loading: { true: "opacity-50 cursor-not-allowed", false: "" }
156
+ }
157
+ })
102
158
  ```
103
159
 
104
160
  ---
105
161
 
106
- ## Responsive Props
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
+ ```
107
185
 
108
- Semua props mendukung responsive object:
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
+ ```
109
191
 
192
+ ### Responsive Object
110
193
  ```tsx
194
+ // Semua prop mendukung responsive object
111
195
  <Box
112
196
  p={{ base: 2, sm: 4, md: 6, lg: 8 }}
113
- cols={{ base: 1, sm: 2, lg: 3 }}
114
- gap={{ base: 3, md: 4 }}
115
- display="grid"
197
+ cols={{ base: 1, md: 2, lg: 3 }}
198
+ hidden={{ base: true, md: false }}
116
199
  />
117
- // โ†’ p-2 sm:p-4 md:p-6 lg:p-8 grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 md:gap-4
118
- ```
119
-
120
- ### Available Props
121
-
122
- | Category | Props |
123
- |----------|-------|
124
- | **Padding** | `p` `px` `py` `pt` `pb` `pl` `pr` |
125
- | **Margin** | `m` `mx` `my` `mt` `mb` `ml` `mr` |
126
- | **Gap** | `gap` `gapX` `gapY` `spaceX` `spaceY` |
127
- | **Sizing** | `w` `h` `size` `minW` `maxW` `minH` `maxH` |
128
- | **Colors** | `bg` `text` `border` `ring` `fill` `stroke` |
129
- | **Typography** | `font` `fontSize` `leading` `tracking` `align` `truncate` `lineClamp` |
130
- | **Flexbox** | `flex` `flexDir` `items` `justify` `wrap` `grow` `shrink` `basis` `self` |
131
- | **Grid** | `cols` `rows` `colSpan` `rowSpan` `colStart` `colEnd` |
132
- | **Layout** | `display` `overflow` `pos` `z` `top` `right` `bottom` `left` `inset` |
133
- | **Border** | `rounded` `roundedT` `roundedB` `borderStyle` `outline` |
134
- | **Effects** | `opacity` `shadow` `blur` `brightness` `contrast` `grayscale` `backdrop` |
135
- | **Motion** | `transition` `duration` `ease` `delay` `animate` |
136
- | **Interaction** | `cursor` `select` `pointer` `touch` |
200
+ ```
137
201
 
138
202
  ---
139
203
 
140
- ## Zero-Runtime (Next.js)
141
-
142
- Di production build, semua `tw.tag\`...\`` dikompilasi menjadi static string โ€” zero JS runtime overhead.
204
+ ## ๐ŸŽจ Theme & Design Tokens
143
205
 
144
- ```ts
145
- // next.config.ts
146
- import { withTailwindStyled } from "tailwind-styled-v4/compiler"
206
+ ### Setup
147
207
 
148
- export default withTailwindStyled({
149
- mode: "zero-runtime", // "runtime" di dev, "zero-runtime" di production
150
- addDataAttr: true, // data-tw="tw-3f2a1" untuk DevTools
151
- generateSafelist: true, // auto-generate tailwind.safelist.json
152
- })(/** nextConfig */ {})
208
+ ```tsx
209
+ // app/layout.tsx
210
+ import { loadDefaultTheme } from "tailwind-styled-v4"
211
+ loadDefaultTheme()
153
212
  ```
154
213
 
155
- **Before (source):**
214
+ ### Custom Theme
215
+
156
216
  ```tsx
157
- const Box = tw.div`p-4 bg-zinc-900 rounded-xl`
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
+ })
158
244
  ```
159
245
 
160
- **After (compiled output):**
246
+ ### Penggunaan Token
247
+
161
248
  ```tsx
162
- const Box = styled.div.attrs(() => ({ className: "p-4 bg-zinc-900 rounded-xl" }))``
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
163
254
  ```
164
255
 
165
- Runtime cost: **0 bytes** untuk class resolution.
166
-
167
256
  ---
168
257
 
169
- ## Zero-Runtime (Vite)
258
+ ## ๐ŸŽฏ cv() โ€” Standalone Class Variants
170
259
 
171
- ```ts
172
- // vite.config.ts
173
- import { tailwindStyledPlugin } from "tailwind-styled-v4/compiler"
260
+ Cocok untuk **shadcn/ui**, **Radix UI**, atau komponen headless apapun tanpa styled-components.
174
261
 
175
- export default defineConfig({
176
- plugins: [react(), tailwindStyledPlugin({ mode: "zero-runtime" })],
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" },
177
282
  })
283
+
284
+ // Pakai di komponen apapun
285
+ function Button({ variant, size, className, ...props }) {
286
+ return <button className={buttonVariants({ variant, size, className })} {...props} />
287
+ }
178
288
  ```
179
289
 
180
290
  ---
181
291
 
182
- ## Plugin System
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
183
335
 
184
336
  ```tsx
185
337
  import { registerPlugin } from "tailwind-styled-v4"
186
338
 
187
- // Custom plugin
188
339
  registerPlugin({
189
340
  name: "glass",
190
341
  props: {
191
- glass: v => v === true ? "bg-white/10 backdrop-blur-xl border border-white/10" : "",
192
- frosted: v => v === true ? "bg-white/5 backdrop-blur-md saturate-150" : "",
193
- }
342
+ glass: (v) => v === true
343
+ ? "backdrop-blur-md bg-white/10 border border-white/20"
344
+ : `backdrop-blur-${v} bg-white/10`,
345
+ },
194
346
  })
195
347
 
196
- // Usage
197
- <Box glass /> // โ†’ bg-white/10 backdrop-blur-xl border border-white/10
198
- <Box frosted /> // โ†’ bg-white/5 backdrop-blur-md saturate-150
348
+ // Langsung bisa dipakai
349
+ <Box glass />
350
+ <Box glass="xl" />
199
351
  ```
200
352
 
201
- ### Built-in Plugins
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
+ ```
202
382
 
383
+ **Before:**
203
384
  ```tsx
204
- import { loadBuiltinPlugins } from "tailwind-styled-v4"
205
- loadBuiltinPlugins() // animation, grid, backdrop, gradient, scroll, print
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" }))``
206
391
  ```
207
392
 
393
+ > ๐Ÿš€ Nol JS runtime cost โ€” `className` jadi static string, bukan computed di setiap render.
394
+
208
395
  ---
209
396
 
210
- ## Theme System
397
+ ## ๐Ÿ›ก๏ธ shouldForwardProp
398
+
399
+ Semua tw props **otomatis diblokir** dari forwarding ke DOM. Tidak ada warning `Unknown prop` di console.
211
400
 
212
401
  ```tsx
213
- import { setTheme, loadDefaultTheme, loadDarkTheme } from "tailwind-styled-v4"
402
+ // โœ… Tidak ada warning di DOM
403
+ <Box p={4} flex cols={3} bg="zinc-900" />
214
404
 
215
- // Preset themes
216
- loadDefaultTheme() // semantic tokens: primary, surface, muted, dll
217
- loadDarkTheme() // fintech/crypto/Web3 dark preset
405
+ // Transient props ($) selalu di-forward
406
+ <Box $isActive={true} />
218
407
 
219
- // Custom tokens
220
- setTheme({
221
- colors: {
222
- brand: "blue-600",
223
- "brand-hover": "blue-700",
224
- surface: "zinc-900",
225
- },
226
- radius: {
227
- card: "2xl",
228
- button: "lg",
229
- },
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: "..." },
230
424
  })
231
425
 
232
- // Now works everywhere
233
- <Box bg="brand" rounded="card" />
234
- // โ†’ bg-blue-600 rounded-2xl
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.
235
431
  ```
236
432
 
433
+ > โš ๏ธ Validation hanya aktif di `NODE_ENV !== "production"` โ€” **zero overhead di production**.
434
+
237
435
  ---
238
436
 
239
- ## Safelist Generation
437
+ ## ๐Ÿ”ง Utils
240
438
 
241
- ```bash
242
- npm run build:safelist
243
- ```
439
+ ### cx / cxm
244
440
 
245
- Menghasilkan `tailwind.safelist.json` yang bisa direferensikan di `tailwind.config.ts`:
441
+ ```tsx
442
+ import { cx, cxm } from "tailwind-styled-v4"
246
443
 
247
- ```ts
248
- // tailwind.config.ts
249
- import safelist from "./tailwind.safelist.json"
444
+ // cx โ€” lightweight class joiner
445
+ cx("p-4", isActive && "bg-blue-500", ["rounded", "shadow"])
446
+ cx({ "p-4": true, "hidden": false })
250
447
 
251
- export default {
252
- safelist: safelist.safelist,
253
- // ...
254
- }
448
+ // cxm โ€” cx + tailwind-merge (conflict resolution)
449
+ cxm("p-2", "p-4") // โ†’ "p-4" (p-2 dihapus)
255
450
  ```
256
451
 
257
- ---
452
+ ### Class Parser
258
453
 
259
- ## IntelliSense (VSCode)
454
+ ```tsx
455
+ import { parseClassString, splitModifiers, groupByModifier } from "tailwind-styled-v4"
260
456
 
261
- Library ini menyertakan **58.055 class definitions** di `src/types/tailwind.generated.ts` untuk autocomplete penuh di VSCode termasuk semua modifier:
457
+ splitModifiers("p-4 hover:bg-blue-500 md:p-8")
458
+ // โ†’ { base: ["p-4"], modifiers: ["hover:bg-blue-500", "md:p-8"] }
262
459
 
263
- ```
264
- bg-blue-500 โ† base class
265
- hover:bg-blue-500 โ† hover state
266
- md:bg-blue-500 โ† responsive
267
- dark:bg-blue-500 โ† dark mode
268
- dark:hover:bg-blue-500 โ† combined
460
+ groupByModifier("p-4 hover:bg-blue-500 hover:text-white")
461
+ // โ†’ { "": ["p-4"], hover: ["bg-blue-500", "text-white"] }
269
462
  ```
270
463
 
271
- Tambahkan ke `.vscode/settings.json`:
272
- ```json
273
- {
274
- "tailwindCSS.experimental.classRegex": [
275
- ["tw\\.\\w+`([^`]*)`", "([^`]*)"],
276
- ["tw\\.\\w+\\(([^)]*)\\)", "\"([^\"]*)\""],
277
- ["cv\\(([^)]*)\\)", "\"([^\"]*)\""]
278
- ]
279
- }
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
280
475
  ```
281
476
 
282
477
  ---
283
478
 
284
- ## Scripts
479
+ ## ๐Ÿ“ Struktur Engine
285
480
 
286
- ```bash
287
- npm run build # build library
288
- npm run build:types # regenerate 58k class types
289
- npm run build:safelist # scan src/ dan generate safelist
290
- npm run dev # watch mode
291
- npm run test # run all tests
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
292
526
  ```
293
527
 
294
528
  ---
295
529
 
296
- ## Comparison
297
-
298
- | Feature | tailwind-styled-v4 | twin.macro | CVA | Stitches |
299
- |---------|--------------------|------------|-----|----------|
300
- | Template literal | โœ… | โœ… | โŒ | โŒ |
301
- | Object config + variants | โœ… | โŒ | โœ… | โœ… |
302
- | Compound variants | โœ… | โŒ | โœ… | โœ… |
303
- | Responsive props | โœ… | โŒ | โŒ | โŒ |
304
- | Zero-runtime | โœ… | โœ… | โŒ | โœ… |
305
- | Plugin system | โœ… | โŒ | โŒ | โŒ |
306
- | styled-components compat | โœ… | โœ… | โŒ | โŒ |
307
- | shadcn/ui compat | โœ… | โŒ | โœ… | โŒ |
308
- | Theme/token system | โœ… | โŒ | โŒ | โœ… |
309
- | IntelliSense 58k classes | โœ… | โœ… | โŒ | โŒ |
310
- | Wrap any component | โœ… | โŒ | โŒ | โŒ |
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
+ ```
311
561
 
312
562
  ---
313
563
 
314
- ## Stack Compatibility
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
+ ```
315
594
 
316
- - **Next.js** 15 / 16 (App Router + Pages Router)
317
- - **React** 18 / 19 (Server Components compatible)
318
- - **Tailwind CSS** v3 / v4
319
- - **styled-components** v5 / v6
320
- - **TypeScript** 4.9 / 5.x
321
- - **shadcn/ui** (semua komponen)
322
- - **Radix UI** (via `withTw()`)
323
- - **Vite** 4 / 5
324
- - **Webpack** 4 / 5
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
+ ```
325
603
 
326
604
  ---
327
605
 
328
- ## License
606
+ ## ๐Ÿ“ License
329
607
 
330
- MIT
608
+ MIT ยฉ tailwind-styled-v4