vibe-design-system 1.9.4 → 1.9.5

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "1.9.4",
3
+ "version": "1.9.5",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,11 +20,7 @@ const VDS_OUTPUT = path.join(PROJECT_ROOT, "vds-output.json");
20
20
  const SRC_DIR = path.join(PROJECT_ROOT, "src");
21
21
  const STORIES_DIR = path.join(SRC_DIR, "stories");
22
22
 
23
- // Top of every story file for CSS variables
24
- const STORY_CSS_HEADER = `// @ts-ignore
25
- import '../index.css'
26
-
27
- `;
23
+ // CSS is loaded from .storybook/preview.tsx never add CSS import to story files.
28
24
 
29
25
  // Components we don't want to auto-generate stories for (project-specific dashboards, heavy UIs, etc.)
30
26
  const SKIP_LIST = [
@@ -48,6 +44,264 @@ const SKIP_LIST = [
48
44
  "Sonner",
49
45
  ];
50
46
 
47
+ /** shadcn/ui composite component recipes: component name → imports + render. */
48
+ const RECIPES = {
49
+ Accordion: {
50
+ imports: ["AccordionItem", "AccordionTrigger", "AccordionContent"],
51
+ render: `(args) => (
52
+ <ComponentRef type="single" collapsible {...args}>
53
+ <AccordionItem value="item-1">
54
+ <AccordionTrigger>Is it accessible?</AccordionTrigger>
55
+ <AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
56
+ </AccordionItem>
57
+ <AccordionItem value="item-2">
58
+ <AccordionTrigger>Is it styled?</AccordionTrigger>
59
+ <AccordionContent>Yes. It comes with default styles.</AccordionContent>
60
+ </AccordionItem>
61
+ </ComponentRef>
62
+ )`,
63
+ },
64
+ Dialog: {
65
+ imports: ["DialogTrigger", "DialogContent", "DialogHeader", "DialogTitle", "DialogDescription"],
66
+ render: `(args) => (
67
+ <ComponentRef {...args}>
68
+ <DialogTrigger asChild><button>Open Dialog</button></DialogTrigger>
69
+ <DialogContent>
70
+ <DialogHeader>
71
+ <DialogTitle>Dialog Title</DialogTitle>
72
+ <DialogDescription>This is a dialog description.</DialogDescription>
73
+ </DialogHeader>
74
+ </DialogContent>
75
+ </ComponentRef>
76
+ )`,
77
+ },
78
+ AlertDialog: {
79
+ imports: ["AlertDialogTrigger", "AlertDialogContent", "AlertDialogHeader", "AlertDialogTitle", "AlertDialogDescription", "AlertDialogFooter", "AlertDialogCancel", "AlertDialogAction"],
80
+ render: `(args) => (
81
+ <ComponentRef {...args}>
82
+ <AlertDialogTrigger asChild><button>Open</button></AlertDialogTrigger>
83
+ <AlertDialogContent>
84
+ <AlertDialogHeader>
85
+ <AlertDialogTitle>Are you sure?</AlertDialogTitle>
86
+ <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
87
+ </AlertDialogHeader>
88
+ <AlertDialogFooter>
89
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
90
+ <AlertDialogAction>Continue</AlertDialogAction>
91
+ </AlertDialogFooter>
92
+ </AlertDialogContent>
93
+ </ComponentRef>
94
+ )`,
95
+ },
96
+ Alert: {
97
+ imports: ["AlertTitle", "AlertDescription"],
98
+ render: `(args) => (
99
+ <ComponentRef {...args}>
100
+ <AlertTitle>Heads up!</AlertTitle>
101
+ <AlertDescription>You can add components to your app using the CLI.</AlertDescription>
102
+ </ComponentRef>
103
+ )`,
104
+ },
105
+ Tooltip: {
106
+ imports: ["TooltipProvider", "TooltipTrigger", "TooltipContent"],
107
+ render: `(args) => (
108
+ <TooltipProvider>
109
+ <ComponentRef {...args}>
110
+ <TooltipTrigger asChild><button>Hover me</button></TooltipTrigger>
111
+ <TooltipContent><p>Tooltip content</p></TooltipContent>
112
+ </ComponentRef>
113
+ </TooltipProvider>
114
+ )`,
115
+ },
116
+ Popover: {
117
+ imports: ["PopoverTrigger", "PopoverContent"],
118
+ render: `(args) => (
119
+ <ComponentRef {...args}>
120
+ <PopoverTrigger asChild><button>Open Popover</button></PopoverTrigger>
121
+ <PopoverContent>Place content here.</PopoverContent>
122
+ </ComponentRef>
123
+ )`,
124
+ },
125
+ HoverCard: {
126
+ imports: ["HoverCardTrigger", "HoverCardContent"],
127
+ render: `(args) => (
128
+ <ComponentRef {...args}>
129
+ <HoverCardTrigger asChild><button>Hover</button></HoverCardTrigger>
130
+ <HoverCardContent>Card content on hover.</HoverCardContent>
131
+ </ComponentRef>
132
+ )`,
133
+ },
134
+ Tabs: {
135
+ imports: ["TabsList", "TabsTrigger", "TabsContent"],
136
+ render: `(args) => (
137
+ <ComponentRef defaultValue="tab1" {...args}>
138
+ <TabsList>
139
+ <TabsTrigger value="tab1">Tab 1</TabsTrigger>
140
+ <TabsTrigger value="tab2">Tab 2</TabsTrigger>
141
+ </TabsList>
142
+ <TabsContent value="tab1">Content for tab 1</TabsContent>
143
+ <TabsContent value="tab2">Content for tab 2</TabsContent>
144
+ </ComponentRef>
145
+ )`,
146
+ },
147
+ Table: {
148
+ imports: ["TableHeader", "TableBody", "TableRow", "TableHead", "TableCell"],
149
+ render: `(args) => (
150
+ <ComponentRef {...args}>
151
+ <TableHeader><TableRow><TableHead>Name</TableHead><TableHead>Status</TableHead></TableRow></TableHeader>
152
+ <TableBody><TableRow><TableCell>Item 1</TableCell><TableCell>Active</TableCell></TableRow></TableBody>
153
+ </ComponentRef>
154
+ )`,
155
+ },
156
+ DropdownMenu: {
157
+ imports: ["DropdownMenuTrigger", "DropdownMenuContent", "DropdownMenuItem"],
158
+ render: `(args) => (
159
+ <ComponentRef {...args}>
160
+ <DropdownMenuTrigger asChild><button>Open Menu</button></DropdownMenuTrigger>
161
+ <DropdownMenuContent>
162
+ <DropdownMenuItem>Profile</DropdownMenuItem>
163
+ <DropdownMenuItem>Settings</DropdownMenuItem>
164
+ <DropdownMenuItem>Logout</DropdownMenuItem>
165
+ </DropdownMenuContent>
166
+ </ComponentRef>
167
+ )`,
168
+ },
169
+ ContextMenu: {
170
+ imports: ["ContextMenuTrigger", "ContextMenuContent", "ContextMenuItem"],
171
+ render: `(args) => (
172
+ <ComponentRef {...args}>
173
+ <ContextMenuTrigger><div style={{padding: 40, border: '1px dashed #ccc'}}>Right click here</div></ContextMenuTrigger>
174
+ <ContextMenuContent>
175
+ <ContextMenuItem>Back</ContextMenuItem>
176
+ <ContextMenuItem>Forward</ContextMenuItem>
177
+ <ContextMenuItem>Reload</ContextMenuItem>
178
+ </ContextMenuContent>
179
+ </ComponentRef>
180
+ )`,
181
+ },
182
+ Select: {
183
+ imports: ["SelectTrigger", "SelectValue", "SelectContent", "SelectItem"],
184
+ render: `(args) => (
185
+ <ComponentRef {...args}>
186
+ <SelectTrigger className="w-[180px]"><SelectValue placeholder="Select a fruit" /></SelectTrigger>
187
+ <SelectContent>
188
+ <SelectItem value="apple">Apple</SelectItem>
189
+ <SelectItem value="banana">Banana</SelectItem>
190
+ <SelectItem value="orange">Orange</SelectItem>
191
+ </SelectContent>
192
+ </ComponentRef>
193
+ )`,
194
+ },
195
+ Card: {
196
+ imports: ["CardHeader", "CardTitle", "CardDescription", "CardContent", "CardFooter"],
197
+ render: `(args) => (
198
+ <ComponentRef className="w-[340px]" {...args}>
199
+ <CardHeader>
200
+ <CardTitle>Card title</CardTitle>
201
+ <CardDescription>Short description.</CardDescription>
202
+ </CardHeader>
203
+ <CardContent><p>Card body content here.</p></CardContent>
204
+ <CardFooter>Footer</CardFooter>
205
+ </ComponentRef>
206
+ )`,
207
+ },
208
+ Avatar: {
209
+ imports: ["AvatarImage", "AvatarFallback"],
210
+ render: `(args) => (
211
+ <ComponentRef {...args}>
212
+ <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
213
+ <AvatarFallback>JD</AvatarFallback>
214
+ </ComponentRef>
215
+ )`,
216
+ },
217
+ Checkbox: {
218
+ extraImports: [{ from: "@/components/ui/label", names: ["Label"] }],
219
+ render: `(args) => (
220
+ <div className="flex items-center space-x-2">
221
+ <ComponentRef id="terms" {...args} />
222
+ <Label htmlFor="terms">Accept terms</Label>
223
+ </div>
224
+ )`,
225
+ },
226
+ Switch: {
227
+ extraImports: [{ from: "@/components/ui/label", names: ["Label"] }],
228
+ render: `(args) => (
229
+ <div className="flex items-center space-x-2">
230
+ <ComponentRef id="switch" {...args} />
231
+ <Label htmlFor="switch">Enable notifications</Label>
232
+ </div>
233
+ )`,
234
+ },
235
+ RadioGroup: {
236
+ imports: ["RadioGroupItem"],
237
+ extraImports: [{ from: "@/components/ui/label", names: ["Label"] }],
238
+ render: `(args) => (
239
+ <ComponentRef className="flex flex-col space-y-2" {...args}>
240
+ <div className="flex items-center space-x-2">
241
+ <RadioGroupItem value="comfortable" id="comfortable" />
242
+ <Label htmlFor="comfortable">Comfortable</Label>
243
+ </div>
244
+ <div className="flex items-center space-x-2">
245
+ <RadioGroupItem value="compact" id="compact" />
246
+ <Label htmlFor="compact">Compact</Label>
247
+ </div>
248
+ </ComponentRef>
249
+ )`,
250
+ },
251
+ Sheet: {
252
+ imports: ["SheetTrigger", "SheetContent", "SheetHeader", "SheetTitle", "SheetDescription"],
253
+ render: `(args) => (
254
+ <ComponentRef {...args}>
255
+ <SheetTrigger asChild><button>Open Sheet</button></SheetTrigger>
256
+ <SheetContent>
257
+ <SheetHeader>
258
+ <SheetTitle>Sheet Title</SheetTitle>
259
+ <SheetDescription>Sheet description here.</SheetDescription>
260
+ </SheetHeader>
261
+ </SheetContent>
262
+ </ComponentRef>
263
+ )`,
264
+ },
265
+ Drawer: {
266
+ imports: ["DrawerTrigger", "DrawerContent", "DrawerHeader", "DrawerTitle", "DrawerDescription"],
267
+ render: `(args) => (
268
+ <ComponentRef {...args}>
269
+ <DrawerTrigger asChild><button>Open Drawer</button></DrawerTrigger>
270
+ <DrawerContent>
271
+ <DrawerHeader>
272
+ <DrawerTitle>Drawer Title</DrawerTitle>
273
+ <DrawerDescription>Drawer description here.</DrawerDescription>
274
+ </DrawerHeader>
275
+ </DrawerContent>
276
+ </ComponentRef>
277
+ )`,
278
+ },
279
+ Collapsible: {
280
+ imports: ["CollapsibleTrigger", "CollapsibleContent"],
281
+ render: `(args) => (
282
+ <ComponentRef {...args}>
283
+ <CollapsibleTrigger asChild><button>Toggle</button></CollapsibleTrigger>
284
+ <CollapsibleContent>Collapsible content here.</CollapsibleContent>
285
+ </ComponentRef>
286
+ )`,
287
+ },
288
+ Menubar: {
289
+ imports: ["MenubarMenu", "MenubarTrigger", "MenubarContent", "MenubarItem"],
290
+ render: `(args) => (
291
+ <ComponentRef {...args}>
292
+ <MenubarMenu>
293
+ <MenubarTrigger>File</MenubarTrigger>
294
+ <MenubarContent>
295
+ <MenubarItem>New</MenubarItem>
296
+ <MenubarItem>Open</MenubarItem>
297
+ <MenubarItem>Save</MenubarItem>
298
+ </MenubarContent>
299
+ </MenubarMenu>
300
+ </ComponentRef>
301
+ )`,
302
+ },
303
+ };
304
+
51
305
  function ensureDir(dir) {
52
306
  if (!fs.existsSync(dir)) {
53
307
  fs.mkdirSync(dir, { recursive: true });
@@ -62,27 +316,22 @@ function needsRouter(source) {
62
316
  return false;
63
317
  }
64
318
 
65
- /** If component has <img and accepts children prop, we should omit children from story args (void element). */
66
- function componentHasImgAndChildren(source) {
319
+ /** Void HTML elements: img, input, hr, br — must not receive children. If component wraps one and has children in props, omit children in story args. */
320
+ function componentWrapsVoidElement(source) {
67
321
  if (!source || typeof source !== "string") return false;
68
- const hasImg = /return\s+<img|<\s*img\s+/.test(source);
69
- if (!hasImg) return false;
322
+ const hasVoid = /return\s+<(?:img|input|hr|br)\b|<\s*(?:img|input|hr|br)\s+/.test(source);
323
+ if (!hasVoid) return false;
70
324
  return /\bchildren\b/.test(source);
71
325
  }
72
326
 
73
327
  function toSafeComponentName(name, file) {
74
- if (!name || typeof name !== "string") {
75
- const base = (file || "").replace(/\.[^.]+$/, "");
76
- const parts = base.split(/[\\/]/g);
77
- const last = parts[parts.length - 1] || "Component";
78
- return last.charAt(0).toUpperCase() + last.slice(1);
328
+ if (name && typeof name === "string") {
329
+ return name.replace(/[^A-Za-z0-9]+/g, " ").trim().replace(/\s+([a-z])/g, (_, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toUpperCase()).replace(/\s+/g, "");
79
330
  }
80
- return name
81
- .replace(/[^A-Za-z0-9]+/g, " ")
82
- .trim()
83
- .replace(/\s+([a-z])/g, (_, c) => c.toUpperCase())
84
- .replace(/^\w/, (c) => c.toUpperCase())
85
- .replace(/\s+/g, "");
331
+ const base = (file || "").replace(/\.[^.]+$/, "");
332
+ const parts = base.split(/[\\/]/g);
333
+ const last = parts[parts.length - 1] || "Component";
334
+ return last.charAt(0).toUpperCase() + last.slice(1);
86
335
  }
87
336
 
88
337
  function parseUnionLiterals(type) {
@@ -174,67 +423,6 @@ function buildSpecialStories(componentName, variants) {
174
423
  return lines.join("\n");
175
424
  }
176
425
 
177
- if (componentName === "Checkbox") {
178
- lines.push(`export const Default: Story = {`);
179
- lines.push(` render: (args) => (`);
180
- lines.push(` <div className="flex items-center space-x-2">`);
181
- lines.push(` <ComponentRef id="terms" {...args} />`);
182
- lines.push(` <Label htmlFor="terms">Accept terms</Label>`);
183
- lines.push(` </div>`);
184
- lines.push(` ),`);
185
- lines.push(`};`);
186
- return lines.join("\n");
187
- }
188
-
189
- if (componentName === "Switch") {
190
- lines.push(`export const Default: Story = {`);
191
- lines.push(` render: (args) => (`);
192
- lines.push(` <div className="flex items-center space-x-2">`);
193
- lines.push(` <ComponentRef id="notifications" {...args} />`);
194
- lines.push(` <Label htmlFor="notifications">Enable notifications</Label>`);
195
- lines.push(` </div>`);
196
- lines.push(` ),`);
197
- lines.push(`};`);
198
- return lines.join("\n");
199
- }
200
-
201
- if (componentName === "RadioGroup") {
202
- lines.push(`export const Default: Story = {`);
203
- lines.push(` render: (args) => (`);
204
- lines.push(` <ComponentRef className="flex flex-col space-y-2" {...args}>`);
205
- lines.push(` <div className="flex items-center space-x-2">`);
206
- lines.push(` <RadioGroupItem value="comfortable" id="comfortable" />`);
207
- lines.push(` <Label htmlFor="comfortable">Comfortable</Label>`);
208
- lines.push(` </div>`);
209
- lines.push(` <div className="flex items-center space-x-2">`);
210
- lines.push(` <RadioGroupItem value="compact" id="compact" />`);
211
- lines.push(` <Label htmlFor="compact">Compact</Label>`);
212
- lines.push(` </div>`);
213
- lines.push(` </ComponentRef>`);
214
- lines.push(` ),`);
215
- lines.push(`};`);
216
- return lines.join("\n");
217
- }
218
-
219
- if (componentName === "Select") {
220
- lines.push(`export const Default: Story = {`);
221
- lines.push(` args: { defaultValue: "apple" },`);
222
- lines.push(` render: (args) => (`);
223
- lines.push(` <ComponentRef {...args}>`);
224
- lines.push(` <SelectTrigger className="w-[180px]">`);
225
- lines.push(` <SelectValue placeholder="Select a fruit" />`);
226
- lines.push(` </SelectTrigger>`);
227
- lines.push(` <SelectContent>`);
228
- lines.push(` <SelectItem value="apple">Apple</SelectItem>`);
229
- lines.push(` <SelectItem value="banana">Banana</SelectItem>`);
230
- lines.push(` <SelectItem value="orange">Orange</SelectItem>`);
231
- lines.push(` </SelectContent>`);
232
- lines.push(` </ComponentRef>`);
233
- lines.push(` ),`);
234
- lines.push(`};`);
235
- return lines.join("\n");
236
- }
237
-
238
426
  if (componentName === "Badge") {
239
427
  const vs = variants && variants.length ? variants : ["default", "secondary", "destructive", "outline"];
240
428
  vs.forEach((v, idx) => {
@@ -250,38 +438,56 @@ function buildSpecialStories(componentName, variants) {
250
438
  return lines.join("\n");
251
439
  }
252
440
 
253
- if (componentName === "Card") {
254
- lines.push(`export const Default: Story = {`);
255
- lines.push(` render: (args) => (`);
256
- lines.push(` <ComponentRef className="w-[340px]" {...args}>`);
257
- lines.push(` <CardHeader>`);
258
- lines.push(` <CardTitle>Card title</CardTitle>`);
259
- lines.push(` <CardDescription>Short description about this card.</CardDescription>`);
260
- lines.push(` </CardHeader>`);
261
- lines.push(` <CardContent>`);
262
- lines.push(` <p>Here is some representative content inside the card body.</p>`);
263
- lines.push(` </CardContent>`);
264
- lines.push(` <CardFooter>Footer content</CardFooter>`);
265
- lines.push(` </ComponentRef>`);
266
- lines.push(` ),`);
267
- lines.push(`};`);
268
- return lines.join("\n");
269
- }
441
+ // Fallback: let the generic variant-based logic or RECIPES handle it
442
+ return "";
443
+ }
270
444
 
271
- if (componentName === "Avatar") {
272
- lines.push(`export const Default: Story = {`);
273
- lines.push(` render: (args) => (`);
274
- lines.push(` <ComponentRef {...args}>`);
275
- lines.push(` <AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />`);
276
- lines.push(` <AvatarFallback>JD</AvatarFallback>`);
277
- lines.push(` </ComponentRef>`);
278
- lines.push(` ),`);
279
- lines.push(`};`);
280
- return lines.join("\n");
445
+ function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe) {
446
+ const lines = [];
447
+ lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
448
+ const useRouterDecorator = needsRouter(source);
449
+ if (exportStyle === "default") {
450
+ lines.push(`import ${componentName} from "${importPath}";`);
451
+ if (recipe.imports?.length) {
452
+ lines.push(`import { ${recipe.imports.join(", ")} } from "${importPath}";`);
453
+ }
454
+ lines.push(`const ComponentRef = ${componentName};`);
455
+ } else if (exportStyle === "named") {
456
+ const names = recipe.imports?.length ? [componentName, ...recipe.imports] : [componentName];
457
+ lines.push(`import { ${names.join(", ")} } from "${importPath}";`);
458
+ lines.push(`const ComponentRef = ${componentName};`);
459
+ } else {
460
+ const defaultAlias = `${componentName}Default`;
461
+ const namedAlias = `${componentName}Named`;
462
+ const extra = recipe.imports?.length ? ", " + recipe.imports.join(", ") : "";
463
+ lines.push(`import ${defaultAlias}, { ${componentName} as ${namedAlias}${extra} } from "${importPath}";`);
464
+ lines.push(`const ComponentRef = ${namedAlias} ?? ${defaultAlias};`);
281
465
  }
282
-
283
- // Fallback: let the generic variant-based logic handle it
284
- return "";
466
+ for (const ext of recipe.extraImports || []) {
467
+ lines.push(`import { ${ext.names.join(", ")} } from "${ext.from}";`);
468
+ }
469
+ if (useRouterDecorator) lines.push(`import { MemoryRouter } from "react-router-dom";`);
470
+ lines.push("");
471
+ lines.push(`const meta = {`);
472
+ lines.push(` title: ${JSON.stringify(title)},`);
473
+ lines.push(` component: ComponentRef,`);
474
+ lines.push(` tags: ["autodocs"],`);
475
+ if (useRouterDecorator) {
476
+ lines.push(` decorators: [(Story) => (`);
477
+ lines.push(` <MemoryRouter>`);
478
+ lines.push(` <Story />`);
479
+ lines.push(` </MemoryRouter>`);
480
+ lines.push(` )],`);
481
+ }
482
+ lines.push(`} satisfies Meta<typeof ComponentRef>;`);
483
+ lines.push("");
484
+ lines.push(`export default meta;`);
485
+ lines.push(`type Story = StoryObj<typeof meta>;`);
486
+ lines.push("");
487
+ lines.push(`export const Default: Story = {`);
488
+ lines.push(` render: ${recipe.render},`);
489
+ lines.push(`};`);
490
+ return lines.join("\n");
285
491
  }
286
492
 
287
493
  function buildStoryFileContent(comp) {
@@ -332,7 +538,11 @@ function buildStoryFileContent(comp) {
332
538
  // ignore
333
539
  }
334
540
  const exportStyle = detectExportStyle(source, componentName);
335
- const omitChildren = componentHasImgAndChildren(source);
541
+ const omitChildren = componentWrapsVoidElement(source);
542
+
543
+ if (RECIPES[componentName]) {
544
+ return buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, RECIPES[componentName]);
545
+ }
336
546
 
337
547
  const lines = [];
338
548
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
@@ -352,26 +562,6 @@ function buildStoryFileContent(comp) {
352
562
  lines.push(`const ComponentRef = ${namedAlias} ?? ${defaultAlias};`);
353
563
  }
354
564
 
355
- // Extra imports for composite components
356
- if (componentName === "Checkbox" || componentName === "Switch" || componentName === "RadioGroup") {
357
- lines.push(`import { Label } from "@/components/ui/label";`);
358
- }
359
- if (componentName === "RadioGroup") {
360
- lines.push(`import { RadioGroupItem } from "@/components/ui/radio-group";`);
361
- }
362
- if (componentName === "Select") {
363
- lines.push(
364
- `import { SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/components/ui/select";`,
365
- );
366
- }
367
- if (componentName === "Avatar") {
368
- lines.push(`import { AvatarImage, AvatarFallback } from "@/components/ui/avatar";`);
369
- }
370
- if (componentName === "Card") {
371
- lines.push(
372
- `import { CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card";`,
373
- );
374
- }
375
565
  const useRouterDecorator = needsRouter(source);
376
566
  if (useRouterDecorator) {
377
567
  lines.push(`import { MemoryRouter } from "react-router-dom";`);
@@ -399,7 +589,7 @@ function buildStoryFileContent(comp) {
399
589
  const specialStories = buildSpecialStories(componentName, variants);
400
590
  if (specialStories) {
401
591
  lines.push(specialStories);
402
- return STORY_CSS_HEADER + lines.join("\n");
592
+ return lines.join("\n");
403
593
  }
404
594
 
405
595
  // Generic variant-based stories (omit children for img/void components)
@@ -430,7 +620,7 @@ function buildStoryFileContent(comp) {
430
620
  }
431
621
  }
432
622
 
433
- return STORY_CSS_HEADER + lines.join("\n");
623
+ return lines.join("\n");
434
624
  }
435
625
 
436
626
  /** Build color entries from foundations.colors (skip _dark; flatten to { name, hex }). */
@@ -453,7 +643,6 @@ function writeFoundationsStories(foundations) {
453
643
 
454
644
  const colorEntries = getColorEntries(foundations?.colors);
455
645
  const colorsContent =
456
- STORY_CSS_HEADER +
457
646
  [
458
647
  "import type { Meta, StoryObj } from \"@storybook/react\";",
459
648
  "",
@@ -487,7 +676,6 @@ function writeFoundationsStories(foundations) {
487
676
  value: Array.isArray(v) ? v.join(", ") : String(v),
488
677
  }));
489
678
  const typoContent =
490
- STORY_CSS_HEADER +
491
679
  [
492
680
  "import type { Meta, StoryObj } from \"@storybook/react\";",
493
681
  "",
@@ -520,7 +708,6 @@ function writeFoundationsStories(foundations) {
520
708
  const brandAssets = foundations?.brand?.assets;
521
709
  const assets = Array.isArray(brandAssets) ? brandAssets : [];
522
710
  const brandContent =
523
- STORY_CSS_HEADER +
524
711
  [
525
712
  "import type { Meta, StoryObj } from \"@storybook/react\";",
526
713
  "",
@@ -597,6 +784,8 @@ function main() {
597
784
  const componentName = toSafeComponentName(comp.name, comp.file);
598
785
  if (onlyName && componentName !== onlyName) continue;
599
786
  if (SKIP_LIST.includes(componentName)) continue;
787
+ const requiredCount = Array.isArray(comp.props) ? comp.props.filter((p) => p.required === true).length : 0;
788
+ if (requiredCount > 3) continue;
600
789
 
601
790
  const storyFileName = `${componentName}.stories.tsx`;
602
791
  const storyPath = path.join(STORIES_DIR, storyFileName);
@@ -607,3 +796,4 @@ function main() {
607
796
  }
608
797
 
609
798
  main();
799
+