vibe-design-system 2.8.18 → 2.8.20
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 +1 -1
- package/vds-core-template/scan.mjs +108 -0
- package/vds-core-template/story-generator.mjs +166 -27
package/package.json
CHANGED
|
@@ -1831,6 +1831,111 @@ function extractTokenUsage() {
|
|
|
1831
1831
|
};
|
|
1832
1832
|
}
|
|
1833
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
|
+
// Build reverse lookup: lowercase hex → color name (for arbitrary-* and inline-* tokens)
|
|
1847
|
+
// e.g. "arbitrary-4f46e5" → hex key "4f46e5"
|
|
1848
|
+
const hexToName = new Map();
|
|
1849
|
+
for (const name of colorNames) {
|
|
1850
|
+
if (name.startsWith("arbitrary-") || name.startsWith("inline-")) {
|
|
1851
|
+
const hexKey = name.replace(/^(arbitrary|inline)-/, "").toLowerCase();
|
|
1852
|
+
hexToName.set(hexKey, name);
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// Single-pass regex: matches bg-primary, text-card-foreground, border-chart-1, etc.
|
|
1857
|
+
const colorUtilRe = /\b(bg|text|border|fill|stroke|ring|from|to|via|outline)-([\w][\w-]*)/g;
|
|
1858
|
+
// Bracket-notation: bg-[#4F46E5], text-[#4f46e5/80], etc.
|
|
1859
|
+
const bracketHexRe = /\b(bg|text|border|fill|stroke|ring|from|to|via|outline)-\[#([0-9a-fA-F]{3,8})(?:\/[\d.]+)?\]/g;
|
|
1860
|
+
// Inline style hex colors: color:"#4F46E5", backgroundColor:'#4f46e5', etc.
|
|
1861
|
+
const inlineHexRe = /(?:color|(?:background|border|fill|stroke|ring)(?:Color)?)\s*[:=]\s*['"]#([0-9a-fA-F]{3,8})['"]/g;
|
|
1862
|
+
|
|
1863
|
+
for (const rel of files) {
|
|
1864
|
+
if (rel.includes("stories")) continue;
|
|
1865
|
+
let content;
|
|
1866
|
+
try {
|
|
1867
|
+
content = fs.readFileSync(path.join(SRC_DIR, rel), "utf-8");
|
|
1868
|
+
} catch (_) { continue; }
|
|
1869
|
+
const componentName = path.basename(rel).replace(/\.(tsx|jsx|ts|js)$/, "");
|
|
1870
|
+
|
|
1871
|
+
let m;
|
|
1872
|
+
// ── Token-based utilities (bg-primary, text-muted-foreground, …) ──
|
|
1873
|
+
const reCopy = new RegExp(colorUtilRe.source, "g");
|
|
1874
|
+
while ((m = reCopy.exec(content)) !== null) {
|
|
1875
|
+
const prefix = m[1];
|
|
1876
|
+
const token = m[2];
|
|
1877
|
+
if (!nameSet.has(token)) continue;
|
|
1878
|
+
if (prefix === "bg") usage[token].bg++;
|
|
1879
|
+
else if (prefix === "text") usage[token].text++;
|
|
1880
|
+
else if (prefix === "border") usage[token].border++;
|
|
1881
|
+
else usage[token].other++;
|
|
1882
|
+
usage[token].topFiles.set(componentName, (usage[token].topFiles.get(componentName) || 0) + 1);
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
// ── Bracket-notation hex utilities (bg-[#4F46E5], …) ──
|
|
1886
|
+
if (hexToName.size > 0) {
|
|
1887
|
+
const brCopy = new RegExp(bracketHexRe.source, "gi");
|
|
1888
|
+
while ((m = brCopy.exec(content)) !== null) {
|
|
1889
|
+
const prefix = m[1].toLowerCase();
|
|
1890
|
+
const hexKey = m[2].toLowerCase();
|
|
1891
|
+
// Normalise 3-char hex → 6-char for matching
|
|
1892
|
+
const normalKey = hexKey.length === 3
|
|
1893
|
+
? hexKey[0] + hexKey[0] + hexKey[1] + hexKey[1] + hexKey[2] + hexKey[2]
|
|
1894
|
+
: hexKey;
|
|
1895
|
+
const name = hexToName.get(normalKey) || hexToName.get(hexKey);
|
|
1896
|
+
if (!name) continue;
|
|
1897
|
+
if (prefix === "bg") usage[name].bg++;
|
|
1898
|
+
else if (prefix === "text") usage[name].text++;
|
|
1899
|
+
else if (prefix === "border") usage[name].border++;
|
|
1900
|
+
else usage[name].other++;
|
|
1901
|
+
usage[name].topFiles.set(componentName, (usage[name].topFiles.get(componentName) || 0) + 1);
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
// ── Inline style hex colors (color: "#4F46E5") ──
|
|
1905
|
+
const inCopy = new RegExp(inlineHexRe.source, "gi");
|
|
1906
|
+
while ((m = inCopy.exec(content)) !== null) {
|
|
1907
|
+
const hexKey = m[1].toLowerCase();
|
|
1908
|
+
const normalKey = hexKey.length === 3
|
|
1909
|
+
? hexKey[0] + hexKey[0] + hexKey[1] + hexKey[1] + hexKey[2] + hexKey[2]
|
|
1910
|
+
: hexKey;
|
|
1911
|
+
const name = hexToName.get(normalKey) || hexToName.get(hexKey);
|
|
1912
|
+
if (!name) continue;
|
|
1913
|
+
usage[name].other++;
|
|
1914
|
+
usage[name].topFiles.set(componentName, (usage[name].topFiles.get(componentName) || 0) + 1);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1919
|
+
const result = {};
|
|
1920
|
+
for (const name of colorNames) {
|
|
1921
|
+
const data = usage[name];
|
|
1922
|
+
const total = data.bg + data.text + data.border + data.other;
|
|
1923
|
+
if (total > 0) {
|
|
1924
|
+
result[name] = {
|
|
1925
|
+
bg: data.bg,
|
|
1926
|
+
text: data.text,
|
|
1927
|
+
border: data.border,
|
|
1928
|
+
total,
|
|
1929
|
+
topFiles: [...data.topFiles.entries()]
|
|
1930
|
+
.sort((a, b) => b[1] - a[1])
|
|
1931
|
+
.slice(0, 5)
|
|
1932
|
+
.map(([n]) => n),
|
|
1933
|
+
};
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
return result;
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1834
1939
|
function extractButtonUsage() {
|
|
1835
1940
|
if (!fs.existsSync(SRC_DIR)) return null;
|
|
1836
1941
|
const files = getAllTsxJsxInDir(SRC_DIR);
|
|
@@ -1943,6 +2048,9 @@ function scan() {
|
|
|
1943
2048
|
if (tokenUsage) {
|
|
1944
2049
|
foundations.tokenUsage = tokenUsage;
|
|
1945
2050
|
}
|
|
2051
|
+
const colorNames = Object.keys(foundations.colors || {}).filter((k) => k !== "_dark");
|
|
2052
|
+
const colorUsage = extractColorUsage(colorNames);
|
|
2053
|
+
if (Object.keys(colorUsage).length > 0) foundations.colorUsage = colorUsage;
|
|
1946
2054
|
const componentSuggestions = extractComponentSuggestions();
|
|
1947
2055
|
const unreleasedSectionCandidates = extractUnreleasedSectionCandidates();
|
|
1948
2056
|
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
|
-
|
|
1262
|
-
|
|
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(
|
|
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={{
|
|
1275
|
-
" {
|
|
1276
|
-
"
|
|
1277
|
-
"
|
|
1278
|
-
"
|
|
1279
|
-
"
|
|
1280
|
-
"
|
|
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
|
-
|
|
1289
|
-
|
|
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 || {};
|