vibe-design-system 2.5.9 → 2.5.11

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/bin/init.js CHANGED
@@ -63,18 +63,21 @@ const preview: Preview = {
63
63
  "Foundations",
64
64
  ["Introduction", "Page to Components", "Colors", "Typography", "Brand", "Icons", "Component Suggestions", "Changelog"],
65
65
  "Layout",
66
- ["Navigation", "Footer", "ScrollToTop"],
67
66
  "Components",
68
67
  "Actions",
69
68
  "Data Display",
70
69
  "Examples",
71
- ["Pages"],
72
70
  ],
73
71
  },
74
72
  },
75
73
  },
76
74
  decorators: [
77
- (Story) => React.createElement(MemoryRouter, null, React.createElement(Story, null)),
75
+ (Story, context) => {
76
+ const needsRouter = context.parameters?.router !== false;
77
+ return needsRouter
78
+ ? React.createElement(MemoryRouter, null, React.createElement(Story, null))
79
+ : React.createElement(Story, null);
80
+ },
78
81
  ],
79
82
  };
80
83
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-design-system",
3
- "version": "2.5.9",
3
+ "version": "2.5.11",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "type": "module",
6
6
  "bin": {
@@ -237,101 +237,11 @@ function compareComponents(prevList, newResults) {
237
237
  return { added, removed, modified };
238
238
  }
239
239
 
240
- const CLASSIFICATION_RULES = [
241
- { keywords: ["analysisdashboard", "componentlibrary"], group: "Sections", category: "Dashboard" },
242
- {
243
- keywords: [
244
- "enterprisepushpanel",
245
- "figmalibrarygenerator",
246
- "integrationguide",
247
- "projectdropzone",
248
- "repoconnect",
249
- "tokensstudioguide",
250
- ],
251
- group: "Sections",
252
- category: "Features",
253
- },
254
- { keywords: ["hover-card"], group: "Feedback", category: "Feedback" },
255
- {
256
- keywords: [
257
- "card",
258
- "table",
259
- "list",
260
- "badge",
261
- "avatar",
262
- "chart",
263
- "accordion",
264
- "tabs",
265
- "collapsible",
266
- "toggle-group",
267
- "carousel",
268
- "scroll-area",
269
- ],
270
- group: "Data Display",
271
- category: "Data Display",
272
- },
273
- { keywords: ["toggle"], group: "Actions", category: "Toggle" },
274
- { keywords: ["button", "cta"], group: "Actions", category: "Button" },
275
- { keywords: ["drawer", "sheet"], group: "Feedback", category: "Overlay" },
276
- {
277
- keywords: [
278
- "modal",
279
- "dialog",
280
- "alert",
281
- "toast",
282
- "toaster",
283
- "sonner",
284
- "tooltip",
285
- "popover",
286
- "hover-card",
287
- "progress",
288
- "skeleton",
289
- ],
290
- group: "Feedback",
291
- category: "Feedback",
292
- },
293
- {
294
- keywords: [
295
- "input",
296
- "form",
297
- "select",
298
- "checkbox",
299
- "textarea",
300
- "label",
301
- "switch",
302
- "radio",
303
- "slider",
304
- "calendar",
305
- "input-otp",
306
- "command",
307
- ],
308
- group: "Forms",
309
- category: "Forms",
310
- },
311
- {
312
- keywords: [
313
- "nav",
314
- "header",
315
- "sidebar",
316
- "menu",
317
- "menubar",
318
- "dropdown-menu",
319
- "navigation-menu",
320
- "context-menu",
321
- "navlink",
322
- "breadcrumb",
323
- "pagination",
324
- ],
325
- group: "Navigation",
326
- category: "Navigation",
327
- },
328
- { keywords: ["separator", "aspect-ratio", "resizable"], group: "Layout", category: "Layout" },
329
- {
330
- keywords: ["hero", "banner", "section", "pricing", "feature", "featuresgrid", "interactivedemo", "modernhero"],
331
- group: "Sections",
332
- category: "Sections",
333
- },
334
- ];
240
+ /** Path-based classification: ui/ → shadcn, components root → Components, else Uncategorized. */
241
+ function classifyByPath(rel) {
242
+ if (rel.startsWith("ui/")) return { group: "shadcn", category: "UI" };
243
+ return { group: "Components", category: "Components" };
244
+ }
335
245
 
336
246
  function getAllComponentFiles(dir, baseDir = dir) {
337
247
  const entries = fs.readdirSync(dir, { withFileTypes: true });
@@ -441,14 +351,6 @@ function extractVdsTags(content) {
441
351
  return Object.keys(tags).length ? tags : null;
442
352
  }
443
353
 
444
- function classifyByFileName(filePath) {
445
- const baseName = path.basename(filePath, path.extname(filePath)).toLowerCase();
446
- for (const rule of CLASSIFICATION_RULES) {
447
- const found = rule.keywords.some((kw) => baseName.includes(kw));
448
- if (found) return { group: rule.group, category: rule.category };
449
- }
450
- return { group: "Uncategorized", category: "Uncategorized" };
451
- }
452
354
 
453
355
  function humanizeName(filePath) {
454
356
  const base = path.basename(filePath, path.extname(filePath));
@@ -872,9 +774,27 @@ function parseCssVarBlock(block) {
872
774
  return out;
873
775
  }
874
776
 
777
+ /** Return only theme.extend.colors from tailwind config (user-defined colors), not the default Tailwind palette. */
778
+ function getTailwindExtendColors() {
779
+ try {
780
+ const twPath = path.join(PROJECT_ROOT, "tailwind.config.js");
781
+ const twPathMjs = path.join(PROJECT_ROOT, "tailwind.config.mjs");
782
+ let config = null;
783
+ if (fs.existsSync(twPath)) {
784
+ config = projectRequire(twPath);
785
+ } else if (fs.existsSync(twPathMjs)) {
786
+ config = projectRequire(twPathMjs);
787
+ }
788
+ if (config && typeof config === "object" && config.default) config = config.default;
789
+ return config?.theme?.extend?.colors ?? null;
790
+ } catch (_) {
791
+ return null;
792
+ }
793
+ }
794
+
875
795
  /** Resolve Tailwind theme for boxShadow, spacing, screens, zIndex, motion. Uses resolveConfig when available. */
876
796
  function getTailwindTheme() {
877
- const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {} };
797
+ const empty = { shadows: {}, spacing: {}, breakpoints: {}, zIndex: {}, transitionDuration: {}, transitionTimingFunction: {}, animation: {}, colors: {} };
878
798
  try {
879
799
  const resolveConfig = projectRequire("tailwindcss/resolveConfig");
880
800
  let config = { content: [{ raw: "", extension: "html" }], theme: {} };
@@ -896,6 +816,7 @@ function getTailwindTheme() {
896
816
  const transitionDuration = theme.transitionDuration;
897
817
  const transitionTimingFunction = theme.transitionTimingFunction;
898
818
  const animation = theme.animation;
819
+ const themeColors = theme.colors;
899
820
  const toObj = (v) => (v && typeof v === "object" && !Array.isArray(v) ? v : {});
900
821
  return {
901
822
  shadows: toObj(boxShadow),
@@ -905,6 +826,7 @@ function getTailwindTheme() {
905
826
  transitionDuration: toObj(transitionDuration),
906
827
  transitionTimingFunction: toObj(transitionTimingFunction),
907
828
  animation: toObj(animation),
829
+ colors: toObj(themeColors),
908
830
  };
909
831
  } catch (_) {
910
832
  return empty;
@@ -919,8 +841,15 @@ function extractFoundations() {
919
841
  const borderRadiusScale = {};
920
842
  const cssPath = path.join(PROJECT_ROOT, "src", "index.css");
921
843
  const globalsCss = path.join(PROJECT_ROOT, "src", "globals.css");
844
+ const stylesGlobals = path.join(PROJECT_ROOT, "src", "styles", "globals.css");
922
845
  const appGlobals = path.join(PROJECT_ROOT, "app", "globals.css");
923
- const cssToRead = fs.existsSync(cssPath) ? cssPath : fs.existsSync(globalsCss) ? globalsCss : appGlobals;
846
+ const cssToRead = fs.existsSync(cssPath)
847
+ ? cssPath
848
+ : fs.existsSync(globalsCss)
849
+ ? globalsCss
850
+ : fs.existsSync(stylesGlobals)
851
+ ? stylesGlobals
852
+ : appGlobals;
924
853
 
925
854
  try {
926
855
  if (fs.existsSync(cssToRead)) {
@@ -1130,6 +1059,19 @@ function extractFoundations() {
1130
1059
  const foundationsColors = { ...colors };
1131
1060
  if (Object.keys(colorsDark).length > 0) foundationsColors._dark = colorsDark;
1132
1061
 
1062
+ const extendColors = getTailwindExtendColors();
1063
+ if (extendColors && typeof extendColors === "object") {
1064
+ for (const [key, val] of Object.entries(extendColors)) {
1065
+ if (typeof val === "string" && (val.startsWith("#") || /^[a-z]/.test(val))) {
1066
+ foundationsColors[key] = { value: val, hex: val.startsWith("#") ? val : val };
1067
+ } else if (typeof val === "object" && val !== null && !Array.isArray(val)) {
1068
+ for (const [k, v] of Object.entries(val)) {
1069
+ if (typeof v === "string") foundationsColors[`${key}-${k}`] = { value: v, hex: v.startsWith("#") ? v : v };
1070
+ }
1071
+ }
1072
+ }
1073
+ }
1074
+
1133
1075
  const twTheme = getTailwindTheme();
1134
1076
  const normalizeThemeObj = (obj) => {
1135
1077
  if (!obj || typeof obj !== "object") return {};
@@ -1173,7 +1115,7 @@ function scan() {
1173
1115
  name = vdsTags.name ?? humanizeName(rel);
1174
1116
  description = vdsTags.description ?? "";
1175
1117
  } else {
1176
- const classified = classifyByFileName(rel);
1118
+ const classified = classifyByPath(rel);
1177
1119
  group = classified.group;
1178
1120
  category = classified.category;
1179
1121
  name = humanizeName(rel);
@@ -1192,7 +1134,7 @@ function scan() {
1192
1134
  results.push({
1193
1135
  file: "pages/" + rel,
1194
1136
  name,
1195
- group: "Sections",
1137
+ group: "Pages",
1196
1138
  category: "Pages",
1197
1139
  description: "",
1198
1140
  tokens,
@@ -350,6 +350,12 @@ function needsRouter(source) {
350
350
  return false;
351
351
  }
352
352
 
353
+ /** Detect if component provides its own router (BrowserRouter or RouterProvider). Preview should not wrap with MemoryRouter. */
354
+ function usesOwnRouter(source) {
355
+ if (!source || typeof source !== "string") return false;
356
+ return /\bBrowserRouter\b|\bRouterProvider\b/.test(source);
357
+ }
358
+
353
359
  /** 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. */
354
360
  function componentWrapsVoidElement(source) {
355
361
  if (!source || typeof source !== "string") return false;
@@ -486,7 +492,6 @@ function buildSpecialStories(componentName, variants) {
486
492
  function buildRecipeStoryContent(comp, componentName, importPath, title, source, exportStyle, recipe) {
487
493
  const lines = [];
488
494
  lines.push(`import type { Meta, StoryObj } from "@storybook/react";`);
489
- const useRouterDecorator = needsRouter(source);
490
495
  if (exportStyle === "default") {
491
496
  lines.push(`import ${componentName} from "${importPath}";`);
492
497
  if (recipe.imports?.length) {
@@ -507,18 +512,14 @@ function buildRecipeStoryContent(comp, componentName, importPath, title, source,
507
512
  for (const ext of recipe.extraImports || []) {
508
513
  lines.push(`import { ${ext.names.join(", ")} } from "${ext.from}";`);
509
514
  }
510
- if (useRouterDecorator) lines.push(`import { MemoryRouter } from "react-router-dom";`);
515
+ const skipPreviewRouter = usesOwnRouter(source);
511
516
  lines.push("");
512
517
  lines.push(`const meta = {`);
513
518
  lines.push(` title: ${JSON.stringify(title)},`);
514
519
  lines.push(` component: ComponentRef,`);
515
520
  lines.push(` tags: ["autodocs"],`);
516
- if (useRouterDecorator) {
517
- lines.push(` decorators: [(Story) => (`);
518
- lines.push(` <MemoryRouter>`);
519
- lines.push(` <Story />`);
520
- lines.push(` </MemoryRouter>`);
521
- lines.push(` )],`);
521
+ if (skipPreviewRouter) {
522
+ lines.push(` parameters: { router: false },`);
522
523
  }
523
524
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
524
525
  lines.push("");
@@ -619,22 +620,14 @@ function buildStoryFileContent(comp) {
619
620
  lines.push(`const ComponentRef = ${namedAlias} ?? ${defaultAlias};`);
620
621
  }
621
622
 
622
- const useRouterDecorator = needsRouter(source);
623
- if (useRouterDecorator) {
624
- lines.push(`import { MemoryRouter } from "react-router-dom";`);
625
- }
626
-
623
+ const skipPreviewRouter = usesOwnRouter(source);
627
624
  lines.push("");
628
625
  lines.push(`const meta = {`);
629
626
  lines.push(` title: ${JSON.stringify(title)},`);
630
627
  lines.push(` component: ComponentRef,`);
631
628
  lines.push(` tags: ["autodocs"],`);
632
- if (useRouterDecorator) {
633
- lines.push(` decorators: [(Story) => (`);
634
- lines.push(` <MemoryRouter>`);
635
- lines.push(` <Story />`);
636
- lines.push(` </MemoryRouter>`);
637
- lines.push(` )],`);
629
+ if (skipPreviewRouter) {
630
+ lines.push(` parameters: { router: false },`);
638
631
  }
639
632
  lines.push(`} satisfies Meta<typeof ComponentRef>;`);
640
633
  lines.push("");
@@ -958,9 +951,11 @@ function main() {
958
951
  }
959
952
 
960
953
  for (const comp of components) {
954
+ if (comp.group === "shadcn" || comp.group === "Uncategorized") continue;
955
+ if (comp.group !== "Components") continue;
956
+
961
957
  const componentName = toSafeComponentName(comp.name, comp.file);
962
958
  if (onlyName && componentName !== onlyName) continue;
963
- if (!comp.file.startsWith("ui/") && !comp.file.startsWith("pages/")) continue;
964
959
 
965
960
  const storyFileName = `${componentName}.stories.tsx`;
966
961
  const storyPath = path.join(STORIES_DIR, storyFileName);