tailwind-styled-v4 1.0.1 → 4.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 (90) hide show
  1. package/dist/animate.cjs +252 -0
  2. package/dist/animate.cjs.map +1 -0
  3. package/dist/animate.d.cts +117 -0
  4. package/dist/animate.d.ts +117 -0
  5. package/dist/animate.js +245 -0
  6. package/dist/animate.js.map +1 -0
  7. package/dist/astTransform-ua-eapqs.d.cts +41 -0
  8. package/dist/astTransform-ua-eapqs.d.ts +41 -0
  9. package/dist/compiler.cjs +3594 -0
  10. package/dist/compiler.cjs.map +1 -0
  11. package/dist/compiler.d.cts +716 -0
  12. package/dist/compiler.d.ts +716 -0
  13. package/dist/compiler.js +3535 -0
  14. package/dist/compiler.js.map +1 -0
  15. package/dist/css.cjs +71 -0
  16. package/dist/css.cjs.map +1 -0
  17. package/dist/css.d.cts +45 -0
  18. package/dist/css.d.ts +45 -0
  19. package/dist/css.js +62 -0
  20. package/dist/css.js.map +1 -0
  21. package/dist/devtools.cjs +959 -0
  22. package/dist/devtools.cjs.map +1 -0
  23. package/dist/devtools.d.cts +22 -0
  24. package/dist/devtools.d.ts +22 -0
  25. package/dist/devtools.js +952 -0
  26. package/dist/devtools.js.map +1 -0
  27. package/dist/index.cjs +1058 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +584 -0
  30. package/dist/index.d.ts +449 -980
  31. package/dist/index.js +1021 -3
  32. package/dist/index.js.map +1 -1
  33. package/dist/next.cjs +268 -0
  34. package/dist/next.cjs.map +1 -0
  35. package/dist/next.d.cts +45 -0
  36. package/dist/next.d.ts +45 -0
  37. package/dist/next.js +261 -0
  38. package/dist/next.js.map +1 -0
  39. package/dist/plugins.cjs +396 -0
  40. package/dist/plugins.cjs.map +1 -0
  41. package/dist/plugins.d.cts +231 -0
  42. package/dist/plugins.d.ts +231 -0
  43. package/dist/plugins.js +381 -0
  44. package/dist/plugins.js.map +1 -0
  45. package/dist/preset.cjs +129 -0
  46. package/dist/preset.cjs.map +1 -0
  47. package/dist/preset.d.cts +249 -0
  48. package/dist/preset.d.ts +249 -0
  49. package/dist/preset.js +124 -0
  50. package/dist/preset.js.map +1 -0
  51. package/dist/theme.cjs +154 -0
  52. package/dist/theme.cjs.map +1 -0
  53. package/dist/theme.d.cts +181 -0
  54. package/dist/theme.d.ts +181 -0
  55. package/dist/theme.js +148 -0
  56. package/dist/theme.js.map +1 -0
  57. package/dist/turbopackLoader.cjs +2689 -0
  58. package/dist/turbopackLoader.cjs.map +1 -0
  59. package/dist/turbopackLoader.d.cts +22 -0
  60. package/dist/turbopackLoader.d.ts +22 -0
  61. package/dist/turbopackLoader.js +2681 -0
  62. package/dist/turbopackLoader.js.map +1 -0
  63. package/dist/vite.cjs +105 -0
  64. package/dist/vite.cjs.map +1 -0
  65. package/dist/vite.d.cts +22 -0
  66. package/dist/vite.d.ts +22 -0
  67. package/dist/vite.js +96 -0
  68. package/dist/vite.js.map +1 -0
  69. package/dist/webpackLoader.cjs +2670 -0
  70. package/dist/webpackLoader.cjs.map +1 -0
  71. package/dist/webpackLoader.d.cts +24 -0
  72. package/dist/webpackLoader.d.ts +24 -0
  73. package/dist/webpackLoader.js +2662 -0
  74. package/dist/webpackLoader.js.map +1 -0
  75. package/package.json +62 -32
  76. package/CHANGELOG.md +0 -75
  77. package/LICENSE +0 -21
  78. package/README.md +0 -608
  79. package/dist/cli/init.js +0 -208
  80. package/dist/compiler/index.d.mts +0 -214
  81. package/dist/compiler/index.d.ts +0 -214
  82. package/dist/compiler/index.js +0 -546
  83. package/dist/compiler/index.js.map +0 -1
  84. package/dist/compiler/index.mjs +0 -504
  85. package/dist/compiler/index.mjs.map +0 -1
  86. package/dist/index.d.mts +0 -1115
  87. package/dist/index.mjs +0 -4
  88. package/dist/index.mjs.map +0 -1
  89. package/dist/turbopack-loader.js +0 -232
  90. 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