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.
Files changed (74) hide show
  1. package/dist/animate.cjs +771 -0
  2. package/dist/animate.cjs.map +1 -0
  3. package/dist/animate.d.cts +73 -0
  4. package/dist/animate.d.ts +73 -0
  5. package/dist/animate.js +752 -0
  6. package/dist/animate.js.map +1 -0
  7. package/dist/chunk-VZEJV27B.js +11 -0
  8. package/dist/chunk-VZEJV27B.js.map +1 -0
  9. package/dist/chunk-Y5D3E72P.cjs +13 -0
  10. package/dist/chunk-Y5D3E72P.cjs.map +1 -0
  11. package/dist/css.cjs +121 -0
  12. package/dist/css.cjs.map +1 -0
  13. package/dist/css.d.cts +30 -0
  14. package/dist/css.d.ts +30 -0
  15. package/dist/css.js +112 -0
  16. package/dist/css.js.map +1 -0
  17. package/dist/devtools.cjs +1071 -0
  18. package/dist/devtools.cjs.map +1 -0
  19. package/dist/devtools.d.cts +22 -0
  20. package/dist/devtools.d.ts +22 -0
  21. package/dist/devtools.js +1064 -0
  22. package/dist/devtools.js.map +1 -0
  23. package/dist/index.cjs +1353 -0
  24. package/dist/index.cjs.map +1 -0
  25. package/dist/index.d.cts +655 -0
  26. package/dist/index.d.ts +508 -968
  27. package/dist/index.js +1304 -3
  28. package/dist/index.js.map +1 -1
  29. package/dist/next.cjs +248 -0
  30. package/dist/next.cjs.map +1 -0
  31. package/dist/next.d.cts +54 -0
  32. package/dist/next.d.ts +54 -0
  33. package/dist/next.js +241 -0
  34. package/dist/next.js.map +1 -0
  35. package/dist/preset.cjs +423 -0
  36. package/dist/preset.cjs.map +1 -0
  37. package/dist/preset.d.cts +276 -0
  38. package/dist/preset.d.ts +276 -0
  39. package/dist/preset.js +416 -0
  40. package/dist/preset.js.map +1 -0
  41. package/dist/turbopackLoader.cjs +37 -0
  42. package/dist/turbopackLoader.cjs.map +1 -0
  43. package/dist/turbopackLoader.d.cts +12 -0
  44. package/dist/turbopackLoader.d.ts +12 -0
  45. package/dist/turbopackLoader.js +35 -0
  46. package/dist/turbopackLoader.js.map +1 -0
  47. package/dist/vite.cjs +138 -0
  48. package/dist/vite.cjs.map +1 -0
  49. package/dist/vite.d.cts +51 -0
  50. package/dist/vite.d.ts +51 -0
  51. package/dist/vite.js +128 -0
  52. package/dist/vite.js.map +1 -0
  53. package/dist/webpackLoader.cjs +51 -0
  54. package/dist/webpackLoader.cjs.map +1 -0
  55. package/dist/webpackLoader.d.cts +17 -0
  56. package/dist/webpackLoader.d.ts +17 -0
  57. package/dist/webpackLoader.js +49 -0
  58. package/dist/webpackLoader.js.map +1 -0
  59. package/package.json +65 -32
  60. package/CHANGELOG.md +0 -75
  61. package/LICENSE +0 -21
  62. package/README.md +0 -608
  63. package/dist/cli/init.js +0 -208
  64. package/dist/compiler/index.d.mts +0 -214
  65. package/dist/compiler/index.d.ts +0 -214
  66. package/dist/compiler/index.js +0 -546
  67. package/dist/compiler/index.js.map +0 -1
  68. package/dist/compiler/index.mjs +0 -504
  69. package/dist/compiler/index.mjs.map +0 -1
  70. package/dist/index.d.mts +0 -1115
  71. package/dist/index.mjs +0 -4
  72. package/dist/index.mjs.map +0 -1
  73. package/dist/turbopack-loader.js +0 -232
  74. 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