vibe-design-system 2.8.16 → 2.8.19

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": "2.8.16",
3
+ "version": "2.8.19",
4
4
  "description": "Auto-generate design systems for vibe coding projects",
5
5
  "homepage": "https://vibedesign.tech",
6
6
  "repository": {
@@ -1730,6 +1730,8 @@ function extractTokenUsage() {
1730
1730
  const radiusCounts = new Map();
1731
1731
  const animateCounts = new Map();
1732
1732
  const textSizeCounts = new Map();
1733
+ // Per-file tracking: token → Map<componentName, count>
1734
+ const textSizeFiles = new Map();
1733
1735
 
1734
1736
  // Spacing utilities: gap-*, p-*, px-*, py-*, pt-*, pb-*, pl-*, pr-*, m-*, mx-*, my-*, mt-*, mb-*, ml-*, mr-*, space-*, w-*, h-*
1735
1737
  const spacingRe = /\b(gap|p|px|py|pt|pb|pl|pr|m|mx|my|mt|mb|ml|mr|space-x|space-y|w|h|size|inset|top|bottom|left|right|min-w|min-h|max-w|max-h)-((?:\d+(\.\d+)?|px|full|screen|auto|fit|max|min|svh|dvh|svw|dvw))\b/g;
@@ -1792,11 +1794,16 @@ function extractTokenUsage() {
1792
1794
  animateCounts.set(key, (animateCounts.get(key) || 0) + 1);
1793
1795
  }
1794
1796
 
1795
- // Text sizes
1797
+ // Text sizes — global count + per-component breakdown
1798
+ const componentName = path.basename(rel).replace(/\.(tsx|jsx|ts|js)$/, "");
1796
1799
  const textSizeUtilReCopy = new RegExp(textSizeUtilRe.source, "g");
1797
1800
  while ((m = textSizeUtilReCopy.exec(content)) !== null) {
1798
1801
  const key = `text-${m[1]}`;
1799
1802
  textSizeCounts.set(key, (textSizeCounts.get(key) || 0) + 1);
1803
+ // Per-file
1804
+ if (!textSizeFiles.has(key)) textSizeFiles.set(key, new Map());
1805
+ const fm = textSizeFiles.get(key);
1806
+ fm.set(componentName, (fm.get(componentName) || 0) + 1);
1800
1807
  }
1801
1808
  }
1802
1809
 
@@ -1812,10 +1819,75 @@ function extractTokenUsage() {
1812
1819
  zIndex: toTop(zIndexCounts, 10),
1813
1820
  radius: toTop(radiusCounts, 10),
1814
1821
  animations: toTop(animateCounts, 8),
1815
- textSizes: toTop(textSizeCounts, 12),
1822
+ textSizes: toTop(textSizeCounts, 12).map(({ token, count }) => ({
1823
+ token,
1824
+ count,
1825
+ // Top 5 components that use this text size most
1826
+ topFiles: [...(textSizeFiles.get(token) || new Map()).entries()]
1827
+ .sort((a, b) => b[1] - a[1])
1828
+ .slice(0, 5)
1829
+ .map(([name]) => name),
1830
+ })),
1816
1831
  };
1817
1832
  }
1818
1833
 
1834
+ function extractColorUsage(colorNames) {
1835
+ if (!fs.existsSync(SRC_DIR)) return {};
1836
+ if (!Array.isArray(colorNames) || colorNames.length === 0) return {};
1837
+ const files = getAllTsxJsxInDir(SRC_DIR);
1838
+ if (!Array.isArray(files) || files.length === 0) return {};
1839
+
1840
+ const nameSet = new Set(colorNames);
1841
+ const usage = {};
1842
+ for (const name of colorNames) {
1843
+ usage[name] = { bg: 0, text: 0, border: 0, other: 0, topFiles: new Map() };
1844
+ }
1845
+
1846
+ // Single-pass regex: matches bg-primary, text-card-foreground, border-chart-1, etc.
1847
+ const colorUtilRe = /\b(bg|text|border|fill|stroke|ring|from|to|via|outline)-([\w][\w-]*)/g;
1848
+
1849
+ for (const rel of files) {
1850
+ if (rel.includes("stories")) continue;
1851
+ let content;
1852
+ try {
1853
+ content = fs.readFileSync(path.join(SRC_DIR, rel), "utf-8");
1854
+ } catch (_) { continue; }
1855
+ const componentName = path.basename(rel).replace(/\.(tsx|jsx|ts|js)$/, "");
1856
+
1857
+ let m;
1858
+ const reCopy = new RegExp(colorUtilRe.source, "g");
1859
+ while ((m = reCopy.exec(content)) !== null) {
1860
+ const prefix = m[1];
1861
+ const token = m[2];
1862
+ if (!nameSet.has(token)) continue;
1863
+ if (prefix === "bg") usage[token].bg++;
1864
+ else if (prefix === "text") usage[token].text++;
1865
+ else if (prefix === "border") usage[token].border++;
1866
+ else usage[token].other++;
1867
+ usage[token].topFiles.set(componentName, (usage[token].topFiles.get(componentName) || 0) + 1);
1868
+ }
1869
+ }
1870
+
1871
+ const result = {};
1872
+ for (const name of colorNames) {
1873
+ const data = usage[name];
1874
+ const total = data.bg + data.text + data.border + data.other;
1875
+ if (total > 0) {
1876
+ result[name] = {
1877
+ bg: data.bg,
1878
+ text: data.text,
1879
+ border: data.border,
1880
+ total,
1881
+ topFiles: [...data.topFiles.entries()]
1882
+ .sort((a, b) => b[1] - a[1])
1883
+ .slice(0, 5)
1884
+ .map(([n]) => n),
1885
+ };
1886
+ }
1887
+ }
1888
+ return result;
1889
+ }
1890
+
1819
1891
  function extractButtonUsage() {
1820
1892
  if (!fs.existsSync(SRC_DIR)) return null;
1821
1893
  const files = getAllTsxJsxInDir(SRC_DIR);
@@ -1928,6 +2000,9 @@ function scan() {
1928
2000
  if (tokenUsage) {
1929
2001
  foundations.tokenUsage = tokenUsage;
1930
2002
  }
2003
+ const colorNames = Object.keys(foundations.colors || {}).filter((k) => k !== "_dark");
2004
+ const colorUsage = extractColorUsage(colorNames);
2005
+ if (Object.keys(colorUsage).length > 0) foundations.colorUsage = colorUsage;
1931
2006
  const componentSuggestions = extractComponentSuggestions();
1932
2007
  const unreleasedSectionCandidates = extractUnreleasedSectionCandidates();
1933
2008
  const output = {
@@ -1240,53 +1240,192 @@ function buildStoryFileContent(comp) {
1240
1240
  return lines.join("\n");
1241
1241
  }
1242
1242
 
1243
- /** Build color entries from foundations.colors (skip _dark; flatten to { name, hex }). */
1244
- function getColorEntries(colors) {
1245
- if (!colors || typeof colors !== "object") return [];
1246
- const entries = [];
1247
- for (const [name, v] of Object.entries(colors)) {
1248
- if (name === "_dark") continue;
1249
- const hex = v && (v.hex ?? (typeof v.value === "string" && v.value.startsWith("#") ? v.value : null));
1250
- const value = v && v.value;
1251
- if (hex) entries.push({ name, hex });
1252
- else if (value) entries.push({ name, hex: value });
1253
- }
1254
- return entries;
1255
- }
1256
-
1257
1243
  function writeFoundationsStories(foundations) {
1258
1244
  const foundationsDir = path.join(STORIES_DIR, "foundations");
1259
1245
  ensureDir(foundationsDir);
1260
1246
 
1261
- const colorEntries = getColorEntries(foundations?.colors);
1262
- const colorsContent =
1263
- [
1247
+ // ── Colors story ──────────────────────────────────────────────────────────
1248
+ {
1249
+ const rawColors = foundations?.colors || {};
1250
+ const colorUsage = foundations?.colorUsage || {};
1251
+
1252
+ const NON_COLOR_KEYS = new Set(["font-weight-medium", "font-weight-normal"]);
1253
+
1254
+ const DESCRIPTIONS = {
1255
+ background: "Page background",
1256
+ foreground: "Default text color",
1257
+ card: "Card / panel background",
1258
+ "card-foreground": "Text on cards",
1259
+ popover: "Popover background",
1260
+ "popover-foreground": "Text in popovers",
1261
+ primary: "Brand color — CTAs, active states",
1262
+ "primary-foreground": "Text on primary backgrounds",
1263
+ secondary: "Secondary actions, subtle UI",
1264
+ "secondary-foreground": "Text on secondary",
1265
+ muted: "Muted backgrounds, disabled",
1266
+ "muted-foreground": "Placeholder, secondary text",
1267
+ accent: "Hover states, highlights",
1268
+ "accent-foreground": "Text on accent backgrounds",
1269
+ destructive: "Errors, delete actions",
1270
+ "destructive-foreground": "Text on destructive",
1271
+ border: "Border / divider color",
1272
+ input: "Input border",
1273
+ "input-background": "Input fill",
1274
+ "switch-background": "Toggle switch track",
1275
+ ring: "Focus ring",
1276
+ "chart-1": "Chart series 1",
1277
+ "chart-2": "Chart series 2",
1278
+ "chart-3": "Chart series 3",
1279
+ "chart-4": "Chart series 4",
1280
+ "chart-5": "Chart series 5",
1281
+ sidebar: "Sidebar background",
1282
+ "sidebar-foreground": "Sidebar text",
1283
+ "sidebar-primary": "Sidebar active item",
1284
+ "sidebar-primary-foreground": "Text on sidebar active",
1285
+ "sidebar-accent": "Sidebar hover / subtle",
1286
+ "sidebar-accent-foreground": "Text on sidebar hover",
1287
+ "sidebar-border": "Sidebar dividers",
1288
+ "sidebar-ring": "Sidebar focus ring",
1289
+ };
1290
+
1291
+ const GROUP_DEFS = [
1292
+ { key: "brand", label: "Brand & Primary", tokens: ["primary", "primary-foreground", "secondary", "secondary-foreground"] },
1293
+ { key: "surfaces", label: "Surfaces", tokens: ["background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "muted", "muted-foreground"] },
1294
+ { key: "ui", label: "UI States", tokens: ["accent", "accent-foreground", "border", "input", "input-background", "switch-background", "ring"] },
1295
+ { key: "status", label: "Status", tokens: ["destructive", "destructive-foreground"] },
1296
+ { key: "charts", label: "Charts", tokens: ["chart-1", "chart-2", "chart-3", "chart-4", "chart-5"] },
1297
+ { key: "sidebar", label: "Sidebar", tokens: ["sidebar", "sidebar-foreground", "sidebar-primary", "sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring"] },
1298
+ ];
1299
+
1300
+ const assignedTokens = new Set();
1301
+ const colorGroups = GROUP_DEFS.map((group) => {
1302
+ const colors = group.tokens
1303
+ .filter((name) => rawColors[name] && !NON_COLOR_KEYS.has(name))
1304
+ .map((name) => {
1305
+ assignedTokens.add(name);
1306
+ const c = rawColors[name];
1307
+ const hex = c?.hex || c?.value || "";
1308
+ const u = colorUsage[name] || null;
1309
+ return { name, hex, cssVar: `--${name}`, description: DESCRIPTIONS[name] || "", usage: u };
1310
+ });
1311
+ return { key: group.key, label: group.label, colors };
1312
+ }).filter((g) => g.colors.length > 0);
1313
+
1314
+ // Arbitrary / one-off colors (not assigned to a group, not css-* noise, not font-weight)
1315
+ const arbitraryColors = Object.entries(rawColors)
1316
+ .filter(([name, c]) =>
1317
+ !assignedTokens.has(name) &&
1318
+ !NON_COLOR_KEYS.has(name) &&
1319
+ !name.startsWith("css-") &&
1320
+ (name.startsWith("arbitrary-") || name.startsWith("inline-") ||
1321
+ (!name.startsWith("css-") && !assignedTokens.has(name)))
1322
+ )
1323
+ .map(([name, c]) => {
1324
+ const hex = c?.hex || c?.value || "";
1325
+ if (!hex) return null;
1326
+ const u = colorUsage[name] || null;
1327
+ return { name, hex, usage: u };
1328
+ })
1329
+ .filter(Boolean)
1330
+ .slice(0, 60); // cap to avoid extremely long stories
1331
+
1332
+ const colorsContent = [
1264
1333
  "import type { Meta, StoryObj } from \"@storybook/react\";",
1265
1334
  "",
1266
1335
  "const meta = { title: \"Foundations/Colors\" } satisfies Meta;",
1267
1336
  "export default meta;",
1268
1337
  "type Story = StoryObj;",
1269
1338
  "",
1270
- `const colors = ${JSON.stringify(colorEntries)};`,
1339
+ `const colorGroups: { key: string; label: string; colors: { name: string; hex: string; cssVar: string; description: string; usage: { bg: number; text: number; border: number; total: number; topFiles: string[] } | null }[] }[] = ${JSON.stringify(colorGroups)};`,
1340
+ `const arbitraryColors: { name: string; hex: string; usage: { total: number } | null }[] = ${JSON.stringify(arbitraryColors)};`,
1341
+ "",
1342
+ "function isLight(hex: string): boolean {",
1343
+ " try {",
1344
+ " const h = hex.replace('#','');",
1345
+ " if (h.length < 3) return true;",
1346
+ " const r = parseInt(h.slice(0,2),16), g = parseInt(h.slice(2,4),16), b = parseInt(h.slice(4,6),16);",
1347
+ " return (r*299 + g*587 + b*114) / 1000 > 155;",
1348
+ " } catch { return true; }",
1349
+ "}",
1271
1350
  "",
1272
1351
  "export const Default: Story = {",
1273
1352
  " render: () => (",
1274
- " <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fill, minmax(160px, 1fr))\", gap: \"1rem\" }}>",
1275
- " {colors.map(({ name, hex }) => (",
1276
- " <div key={name} style={{ display: \"flex\", alignItems: \"center\", gap: 12, flexWrap: \"wrap\" }}>",
1277
- " <div style={{ backgroundColor: hex, width: 48, height: 48, borderRadius: 8, border: \"1px solid #333\", flexShrink: 0 }} />",
1278
- " <div>",
1279
- " <p style={{ margin: 0, fontSize: 12, fontWeight: 500 }}>{name}</p>",
1280
- " <code style={{ fontSize: 11, color: \"#888\" }}>{hex}</code>",
1353
+ " <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, background: \"#f8fafc\", minHeight: 500 }}>",
1354
+ " <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\", color: \"#111\" }}>Colors</h2>",
1355
+ " <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 40px\" }}>Design tokens defined in CSS variables — grouped by role</p>",
1356
+ "",
1357
+ " {colorGroups.map(group => (",
1358
+ " <div key={group.key} style={{ marginBottom: 40 }}>",
1359
+ " <h3 style={{ fontSize: 14, fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.08em\", color: \"#6b7280\", margin: \"0 0 16px\", borderBottom: \"1px solid #e5e7eb\", paddingBottom: 8 }}>{group.label}</h3>",
1360
+ " <div style={{ display: \"grid\", gridTemplateColumns: \"repeat(auto-fill, minmax(210px, 1fr))\", gap: 16 }}>",
1361
+ " {group.colors.map(({ name, hex, cssVar, description, usage }) => {",
1362
+ " const transparent = hex === 'transparent' || hex === 'oklch(1 0 0 / 0)' || hex.includes('0000000') || hex === '#0000';",
1363
+ " const light = isLight(hex.startsWith('#') ? hex : '#888888');",
1364
+ " return (",
1365
+ " <div key={name} style={{ background: \"#fff\", borderRadius: 12, border: \"1px solid #e5e7eb\", overflow: \"hidden\", boxShadow: \"0 1px 3px rgba(0,0,0,0.06)\" }}>",
1366
+ " {/* Swatch */}",
1367
+ " <div style={{ height: 72, background: transparent ? 'repeating-linear-gradient(45deg,#e5e7eb,#e5e7eb 4px,#fff 4px,#fff 10px)' : hex, position: \"relative\", flexShrink: 0 }}>",
1368
+ " {usage && usage.total > 0 && (",
1369
+ " <span style={{ position: \"absolute\", top: 8, right: 8, fontSize: 10, fontWeight: 700, padding: \"2px 7px\", borderRadius: 99, background: light ? \"rgba(0,0,0,0.18)\" : \"rgba(255,255,255,0.25)\", color: light ? \"#fff\" : \"#fff\", backdropFilter: \"blur(2px)\" }}>",
1370
+ " ×{usage.total}",
1371
+ " </span>",
1372
+ " )}",
1373
+ " </div>",
1374
+ " {/* Info */}",
1375
+ " <div style={{ padding: \"12px 14px\" }}>",
1376
+ " <div style={{ fontSize: 12, fontWeight: 700, color: \"#111\", marginBottom: 2 }}>{name}</div>",
1377
+ " <code style={{ fontSize: 10, color: \"#6b7280\", display: \"block\", marginBottom: 2 }}>{cssVar}</code>",
1378
+ " <code style={{ fontSize: 10, color: \"#9ca3af\", display: \"block\", marginBottom: description ? 6 : 0 }}>{hex.length > 28 ? hex.slice(0,28)+'…' : hex}</code>",
1379
+ " {description && <p style={{ margin: \"0 0 8px\", fontSize: 11, color: \"#6b7280\", lineHeight: 1.4 }}>{description}</p>}",
1380
+ " {usage && usage.total > 0 && (",
1381
+ " <div style={{ display: \"flex\", gap: 6, flexWrap: \"wrap\", marginBottom: 6 }}>",
1382
+ " {usage.bg > 0 && <span style={{ fontSize: 10, padding: \"1px 6px\", background: \"#dbeafe\", color: \"#1e40af\", borderRadius: 4 }}>bg ×{usage.bg}</span>}",
1383
+ " {usage.text > 0 && <span style={{ fontSize: 10, padding: \"1px 6px\", background: \"#d1fae5\", color: \"#065f46\", borderRadius: 4 }}>text ×{usage.text}</span>}",
1384
+ " {usage.border > 0 && <span style={{ fontSize: 10, padding: \"1px 6px\", background: \"#fef9c3\", color: \"#854d0e\", borderRadius: 4 }}>border ×{usage.border}</span>}",
1385
+ " {usage.total - usage.bg - usage.text - usage.border > 0 && <span style={{ fontSize: 10, padding: \"1px 6px\", background: \"#f3e8ff\", color: \"#7e22ce\", borderRadius: 4 }}>other ×{usage.total - usage.bg - usage.text - usage.border}</span>}",
1386
+ " </div>",
1387
+ " )}",
1388
+ " {usage && usage.topFiles && usage.topFiles.length > 0 && (",
1389
+ " <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 4 }}>",
1390
+ " {usage.topFiles.map((f: string) => (",
1391
+ " <span key={f} style={{ fontSize: 10, padding: \"1px 6px\", background: \"#f0f9ff\", color: \"#0369a1\", borderRadius: 3, border: \"1px solid #bae6fd\", fontFamily: \"monospace\" }}>{f}</span>",
1392
+ " ))}",
1393
+ " </div>",
1394
+ " )}",
1395
+ " {(!usage || usage.total === 0) && (",
1396
+ " <span style={{ fontSize: 10, color: \"#d1d5db\" }}>Not found in source</span>",
1397
+ " )}",
1398
+ " </div>",
1399
+ " </div>",
1400
+ " );",
1401
+ " })}",
1281
1402
  " </div>",
1282
1403
  " </div>",
1283
1404
  " ))}",
1405
+ "",
1406
+ " {arbitraryColors.length > 0 && (",
1407
+ " <div>",
1408
+ " <h3 style={{ fontSize: 14, fontWeight: 700, textTransform: \"uppercase\", letterSpacing: \"0.08em\", color: \"#6b7280\", margin: \"0 0 16px\", borderBottom: \"1px solid #e5e7eb\", paddingBottom: 8 }}>Arbitrary Colors</h3>",
1409
+ " <p style={{ fontSize: 12, color: \"#9ca3af\", marginBottom: 16, marginTop: -8 }}>One-off hex values used via Tailwind arbitrary values or inline styles in this project</p>",
1410
+ " <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 8 }}>",
1411
+ " {arbitraryColors.map(({ name, hex, usage }) => (",
1412
+ " <div key={name} title={name + ' — ' + hex} style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap: 4, width: 56 }}>",
1413
+ " <div style={{ width: 40, height: 40, borderRadius: 8, background: hex, border: \"1px solid #e5e7eb\", flexShrink: 0 }} />",
1414
+ " <span style={{ fontSize: 9, color: \"#9ca3af\", textAlign: \"center\", wordBreak: \"break-all\" as any, lineHeight: 1.2 }}>{hex.length > 9 ? hex.slice(0,9)+'…' : hex}</span>",
1415
+ " {usage && usage.total > 0 && <span style={{ fontSize: 9, color: \"#6b7280\" }}>×{usage.total}</span>}",
1416
+ " </div>",
1417
+ " ))}",
1418
+ " </div>",
1419
+ " </div>",
1420
+ " )}",
1284
1421
  " </div>",
1285
1422
  " ),",
1286
1423
  "};",
1287
1424
  ].join("\n");
1288
- fs.writeFileSync(path.join(foundationsDir, "Colors.stories.tsx"), colorsContent, "utf-8");
1289
- console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Colors.stories.tsx")));
1425
+
1426
+ fs.writeFileSync(path.join(foundationsDir, "Colors.stories.tsx"), colorsContent, "utf-8");
1427
+ console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Colors.stories.tsx")));
1428
+ }
1290
1429
 
1291
1430
  {
1292
1431
  const typo = foundations?.typography || {};
@@ -1353,24 +1492,35 @@ function writeFoundationsStories(foundations) {
1353
1492
  " ) : (",
1354
1493
  " <div style={{ display: \"flex\", flexDirection: \"column\", gap: 0, marginBottom: 48, border: \"1px solid #e5e7eb\", borderRadius: 10, overflow: \"hidden\" }}>",
1355
1494
  " {typeScale.map(({ step, size, px, lineHeight }, i) => {",
1356
- " const usageCount = usedTextSizes.find(u => u.token === `text-${step}`)?.count;",
1357
- " const remVal = parseFloat(size);",
1358
- " const pxVal = px ? parseInt(px) : Math.round(remVal * 16);",
1495
+ " const usage = usedTextSizes.find(u => u.token === `text-${step}`);",
1496
+ " const usageCount = usage?.count;",
1497
+ " const topFiles = usage?.topFiles || [];",
1359
1498
  " return (",
1360
- " <div key={step} style={{ display: \"flex\", alignItems: \"center\", gap: 20, padding: \"14px 20px\", background: i % 2 === 0 ? \"#fff\" : \"#f9fafb\", borderTop: i === 0 ? \"none\" : \"1px solid #f0f0f0\" }}>",
1361
- " <div style={{ width: 120, flexShrink: 0 }}>",
1362
- " <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>",
1363
- " <code style={{ fontSize: 12, fontWeight: 600, color: \"#6366f1\", background: \"#eef2ff\", padding: \"2px 7px\", borderRadius: 4 }}>text-{step}</code>",
1364
- " {usageCount && <span style={{ fontSize: 11, color: \"#9ca3af\" }}{usageCount}</span>}",
1499
+ " <div key={step} style={{ display: \"flex\", flexDirection: \"column\", padding: \"14px 20px\", background: i % 2 === 0 ? \"#fff\" : \"#f9fafb\", borderTop: i === 0 ? \"none\" : \"1px solid #f0f0f0\" }}>",
1500
+ " <div style={{ display: \"flex\", alignItems: \"center\", gap: 20 }}>",
1501
+ " <div style={{ width: 140, flexShrink: 0 }}>",
1502
+ " <div style={{ display: \"flex\", alignItems: \"center\", gap: 8 }}>",
1503
+ " <code style={{ fontSize: 12, fontWeight: 600, color: \"var(--color-primary, #6366f1)\", background: \"color-mix(in srgb, var(--color-primary, #6366f1) 12%, white)\", padding: \"2px 7px\", borderRadius: 4 }}>text-{step}</code>",
1504
+ " {usageCount && <span style={{ fontSize: 11, color: \"#9ca3af\" }}>×{usageCount}</span>}",
1505
+ " </div>",
1506
+ " <div style={{ fontSize: 11, color: \"#9ca3af\", marginTop: 4 }}>",
1507
+ " {size}{px && px !== size ? ` · ${px}` : \"\"}",
1508
+ " {lineHeight ? <span style={{ marginLeft: 8 }}>lh {lineHeight}</span> : null}",
1509
+ " </div>",
1365
1510
  " </div>",
1366
- " <div style={{ fontSize: 11, color: \"#9ca3af\", marginTop: 4 }}>",
1367
- " {size}{px && px !== size ? ` · ${px}` : \"\"}",
1368
- " {lineHeight ? <span style={{ marginLeft: 8 }}>lh {lineHeight}</span> : null}",
1511
+ " <div style={{ fontSize: size, fontFamily: sansFamily, color: \"#111\", lineHeight: lineHeight || 1.5, overflow: \"hidden\", whiteSpace: \"nowrap\", textOverflow: \"ellipsis\", flex: 1 }}>",
1512
+ " The quick brown fox jumps over the lazy dog",
1369
1513
  " </div>",
1370
1514
  " </div>",
1371
- " <div style={{ fontSize: size, fontFamily: sansFamily, color: \"#111\", lineHeight: lineHeight || 1.5, overflow: \"hidden\", whiteSpace: \"nowrap\", textOverflow: \"ellipsis\", flex: 1 }}>",
1372
- " The quick brown fox jumps over the lazy dog",
1373
- " </div>",
1515
+ " {topFiles.length > 0 && (",
1516
+ " <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 5, marginTop: 8, paddingLeft: 0 }}>",
1517
+ " {topFiles.map((name: string) => (",
1518
+ " <span key={name} style={{ fontSize: 10, background: \"#f0f9ff\", color: \"#0369a1\", padding: \"2px 8px\", borderRadius: 3, border: \"1px solid #bae6fd\", fontFamily: \"monospace\" }}>",
1519
+ " {name}",
1520
+ " </span>",
1521
+ " ))}",
1522
+ " </div>",
1523
+ " )}",
1374
1524
  " </div>",
1375
1525
  " );",
1376
1526
  " })}",
@@ -1607,7 +1757,7 @@ function writeFoundationsStories(foundations) {
1607
1757
  " {spacingTokens.map(({ token, label, barWidth }) => (",
1608
1758
  " <div key={token} style={{ display: \"flex\", alignItems: \"center\", gap: 10, minHeight: 24 }}>",
1609
1759
  " <span style={{ width: 40, fontSize: 11, color: \"#9ca3af\", textAlign: \"right\", flexShrink: 0, fontVariantNumeric: \"tabular-nums\" }}>{token}</span>",
1610
- " <div style={{ width: Math.max(barWidth, 2), height: 16, background: barWidth === 0 ? \"transparent\" : \"linear-gradient(90deg,#818cf8,#6366f1)\", borderRadius: 3, flexShrink: 0, border: barWidth === 0 ? \"1px dashed #555\" : \"none\", boxSizing: \"border-box\" as any }} />",
1760
+ " <div style={{ width: Math.max(barWidth, 2), height: 16, background: barWidth === 0 ? \"transparent\" : \"var(--color-primary, #6366f1)\", opacity: barWidth === 0 ? 1 : 0.85, borderRadius: 3, flexShrink: 0, border: barWidth === 0 ? \"1px dashed #555\" : \"none\", boxSizing: \"border-box\" as any }} />",
1611
1761
  " <code style={{ fontSize: 11, color: \"#6b7280\" }}>{label}</code>",
1612
1762
  " </div>",
1613
1763
  " ))}",
@@ -1744,7 +1894,7 @@ function writeFoundationsStories(foundations) {
1744
1894
  " <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, maxWidth: 380 }}>",
1745
1895
  " {zIndexTokens.map(({ token, value, label }) => (",
1746
1896
  " <div key={token} style={{ display: \"flex\", alignItems: \"center\", gap: 12, padding: \"8px 14px\", background: \"#fff\", borderRadius: 8, border: \"1px solid #e5e7eb\" }}>",
1747
- " <span style={{ width: 36, fontSize: 15, fontWeight: 700, color: \"#6366f1\", textAlign: \"right\", flexShrink: 0, fontVariantNumeric: \"tabular-nums\" }}>{value}</span>",
1897
+ " <span style={{ width: 36, fontSize: 15, fontWeight: 700, color: \"var(--color-primary, #6366f1)\", textAlign: \"right\", flexShrink: 0, fontVariantNumeric: \"tabular-nums\" }}>{value}</span>",
1748
1898
  " <div>",
1749
1899
  " <span style={{ fontSize: 12, fontWeight: 600 }}>{token}</span>",
1750
1900
  " {label ? <span style={{ fontSize: 11, color: \"#9ca3af\", marginLeft: 8 }}>{label}</span> : null}",
@@ -1756,7 +1906,7 @@ function writeFoundationsStories(foundations) {
1756
1906
  " <div style={{ display: \"flex\", flexDirection: \"column\", gap: 6, maxWidth: 380 }}>",
1757
1907
  " {usedZIndex.map(({ token, count }) => (",
1758
1908
  " <div key={token} style={{ display: \"flex\", alignItems: \"center\", gap: 12, padding: \"8px 14px\", background: \"#fff\", borderRadius: 8, border: \"1px solid #e5e7eb\" }}>",
1759
- " <span style={{ width: 36, fontSize: 15, fontWeight: 700, color: \"#6366f1\", textAlign: \"right\", flexShrink: 0 }}>{token.replace('z-','')}</span>",
1909
+ " <span style={{ width: 36, fontSize: 15, fontWeight: 700, color: \"var(--color-primary, #6366f1)\", textAlign: \"right\", flexShrink: 0 }}>{token.replace('z-','')}</span>",
1760
1910
  " <div>",
1761
1911
  " <span style={{ fontSize: 12, fontWeight: 600 }}>{token}</span>",
1762
1912
  " {zSemantics[token] ? <span style={{ fontSize: 11, color: \"#9ca3af\", marginLeft: 8 }}>{zSemantics[token]}</span> : null}",
@@ -1837,7 +1987,7 @@ function writeFoundationsStories(foundations) {
1837
1987
  " <div style={{",
1838
1988
  " width: isFull ? 64 : 72,",
1839
1989
  " height: isFull ? 64 : 56,",
1840
- " background: \"linear-gradient(135deg,#818cf8,#6366f1)\",",
1990
+ " background: \"var(--color-primary, #6366f1)\",",
1841
1991
  " borderRadius: isFull ? \"50%\" : value === \"0px\" || value === \"0\" ? 0 : value,",
1842
1992
  " flexShrink: 0,",
1843
1993
  " }} />",
@@ -1907,7 +2057,7 @@ function writeFoundationsStories(foundations) {
1907
2057
  " position: \"absolute\",",
1908
2058
  " top: 4, left: active ? 172 : 4,",
1909
2059
  " width: 44, height: 24,",
1910
- " background: \"linear-gradient(90deg,#818cf8,#6366f1)\",",
2060
+ " background: \"var(--color-primary, #6366f1)\",",
1911
2061
  " borderRadius: 6,",
1912
2062
  " transition: `left ${value} cubic-bezier(0.4,0,0.2,1)`,",
1913
2063
  " }} />",