vibe-design-system 2.8.14 → 2.8.16
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
|
@@ -1273,6 +1273,39 @@ function extractFoundations() {
|
|
|
1273
1273
|
if (!typography[key]) typography[key] = val;
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
|
+
// ── Type Scale: extract --text-* font sizes + line-heights from compiled CSS
|
|
1277
|
+
const textSteps = ["xs","sm","base","lg","xl","2xl","3xl","4xl","5xl","6xl","7xl","8xl","9xl"];
|
|
1278
|
+
const textSizes = {};
|
|
1279
|
+
const textLineHeights = {};
|
|
1280
|
+
const textSizeRe = new RegExp(`--text-(${textSteps.join("|")})\\s*:\\s*([^;\\n]+);`, "g");
|
|
1281
|
+
const textLhRe = new RegExp(`--text-(${textSteps.join("|")})--line-height\\s*:\\s*([^;\\n]+);`, "g");
|
|
1282
|
+
let tsm;
|
|
1283
|
+
while ((tsm = textSizeRe.exec(css)) !== null) {
|
|
1284
|
+
// Normalize ".75rem" → "0.75rem"
|
|
1285
|
+
const raw = tsm[2].trim();
|
|
1286
|
+
textSizes[tsm[1]] = raw.startsWith(".") ? "0" + raw : raw;
|
|
1287
|
+
}
|
|
1288
|
+
while ((tsm = textLhRe.exec(css)) !== null) {
|
|
1289
|
+
const raw = tsm[2].trim();
|
|
1290
|
+
// Resolve calc(a / b) → rounded ratio string
|
|
1291
|
+
const calcM = raw.match(/calc\(([\d.]+)\s*\/\s*([\d.]+)\)/);
|
|
1292
|
+
if (calcM) {
|
|
1293
|
+
const ratio = parseFloat(calcM[1]) / parseFloat(calcM[2]);
|
|
1294
|
+
textLineHeights[tsm[1]] = (Math.round(ratio * 1000) / 1000) + "";
|
|
1295
|
+
} else {
|
|
1296
|
+
textLineHeights[tsm[1]] = raw;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
const typeScale = textSteps
|
|
1300
|
+
.filter((step) => textSizes[step])
|
|
1301
|
+
.map((step) => {
|
|
1302
|
+
const size = textSizes[step];
|
|
1303
|
+
const pxM = size.match(/^([\d.]+)rem$/);
|
|
1304
|
+
const px = pxM ? Math.round(parseFloat(pxM[1]) * 16) + "px" : null;
|
|
1305
|
+
return { step, size, px, lineHeight: textLineHeights[step] || null };
|
|
1306
|
+
});
|
|
1307
|
+
if (typeScale.length > 0) typography.typeScale = typeScale;
|
|
1308
|
+
|
|
1276
1309
|
// Tailwind v4: extract design tokens from @layer theme { :root { ... } } or bare :root
|
|
1277
1310
|
// Variables: --spacing (base), --radius-*, --shadow-*, --ease-*, --duration-*, --animate-*
|
|
1278
1311
|
const v4ThemeVars = {};
|
|
@@ -1696,6 +1729,7 @@ function extractTokenUsage() {
|
|
|
1696
1729
|
const zIndexCounts = new Map();
|
|
1697
1730
|
const radiusCounts = new Map();
|
|
1698
1731
|
const animateCounts = new Map();
|
|
1732
|
+
const textSizeCounts = new Map();
|
|
1699
1733
|
|
|
1700
1734
|
// Spacing utilities: gap-*, p-*, px-*, py-*, pt-*, pb-*, pl-*, pr-*, m-*, mx-*, my-*, mt-*, mb-*, ml-*, mr-*, space-*, w-*, h-*
|
|
1701
1735
|
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;
|
|
@@ -1707,6 +1741,8 @@ function extractTokenUsage() {
|
|
|
1707
1741
|
const roundedRe = /\brounded(?:-(none|sm|md|lg|xl|2xl|3xl|full|t|r|b|l|tl|tr|br|bl|s|e|ss|se|es|ee|[a-z]+(?:-(?:none|sm|md|lg|xl|2xl|3xl|full))?))?(?!\w)/g;
|
|
1708
1742
|
// Animate utilities: animate-spin, animate-ping, animate-pulse, animate-bounce, animate-none
|
|
1709
1743
|
const animateRe = /\banimate-(spin|ping|pulse|bounce|none|[\w-]+)\b/g;
|
|
1744
|
+
// Text-size utilities: text-xs, text-sm, text-base, text-lg, text-xl, text-2xl, text-3xl...
|
|
1745
|
+
const textSizeUtilRe = /\btext-(xs|sm|base|lg|xl|2xl|3xl|4xl|5xl|6xl|7xl|8xl|9xl)\b/g;
|
|
1710
1746
|
|
|
1711
1747
|
for (const rel of files) {
|
|
1712
1748
|
if (rel.includes("stories")) continue;
|
|
@@ -1755,6 +1791,13 @@ function extractTokenUsage() {
|
|
|
1755
1791
|
const key = `animate-${m[1]}`;
|
|
1756
1792
|
animateCounts.set(key, (animateCounts.get(key) || 0) + 1);
|
|
1757
1793
|
}
|
|
1794
|
+
|
|
1795
|
+
// Text sizes
|
|
1796
|
+
const textSizeUtilReCopy = new RegExp(textSizeUtilRe.source, "g");
|
|
1797
|
+
while ((m = textSizeUtilReCopy.exec(content)) !== null) {
|
|
1798
|
+
const key = `text-${m[1]}`;
|
|
1799
|
+
textSizeCounts.set(key, (textSizeCounts.get(key) || 0) + 1);
|
|
1800
|
+
}
|
|
1758
1801
|
}
|
|
1759
1802
|
|
|
1760
1803
|
const toTop = (map, n) =>
|
|
@@ -1769,6 +1812,7 @@ function extractTokenUsage() {
|
|
|
1769
1812
|
zIndex: toTop(zIndexCounts, 10),
|
|
1770
1813
|
radius: toTop(radiusCounts, 10),
|
|
1771
1814
|
animations: toTop(animateCounts, 8),
|
|
1815
|
+
textSizes: toTop(textSizeCounts, 12),
|
|
1772
1816
|
};
|
|
1773
1817
|
}
|
|
1774
1818
|
|
|
@@ -1288,40 +1288,136 @@ function writeFoundationsStories(foundations) {
|
|
|
1288
1288
|
fs.writeFileSync(path.join(foundationsDir, "Colors.stories.tsx"), colorsContent, "utf-8");
|
|
1289
1289
|
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Colors.stories.tsx")));
|
|
1290
1290
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
const
|
|
1300
|
-
[
|
|
1301
|
-
|
|
1302
|
-
"",
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1291
|
+
{
|
|
1292
|
+
const typo = foundations?.typography || {};
|
|
1293
|
+
const typeScale = Array.isArray(typo.typeScale) ? typo.typeScale : [];
|
|
1294
|
+
const usedTextSizes = (foundations?.tokenUsage?.textSizes || []);
|
|
1295
|
+
const usedTextMap = Object.fromEntries(usedTextSizes.map(({ token, count }) => [token, count]));
|
|
1296
|
+
|
|
1297
|
+
// Font families
|
|
1298
|
+
const fontFamilyKeys = ["fontSans", "fontMono", "body", "heading", "tailwindSans", "tailwindMono"];
|
|
1299
|
+
const familyRows = Object.entries(typo)
|
|
1300
|
+
.filter(([k]) => fontFamilyKeys.includes(k) || k.toLowerCase().includes("font") && !k.toLowerCase().includes("weight") && k !== "typeScale" && k !== "fontSize")
|
|
1301
|
+
.map(([k, v]) => {
|
|
1302
|
+
const val = typeof v === "object" && v !== null ? (v.family || v.value || "") : Array.isArray(v) ? v.join(", ") : String(v);
|
|
1303
|
+
return { token: k, value: val };
|
|
1304
|
+
})
|
|
1305
|
+
.filter((r) => r.value);
|
|
1306
|
+
|
|
1307
|
+
// Font weights
|
|
1308
|
+
const weightKeys = ["fontWeightNormal", "fontWeightMedium", "fontWeightSemibold", "fontWeightBold"];
|
|
1309
|
+
const weightRows = weightKeys
|
|
1310
|
+
.filter((k) => typo[k])
|
|
1311
|
+
.map((k) => ({ token: k, value: String(typo[k]) }));
|
|
1312
|
+
|
|
1313
|
+
// Reverse type scale for display (largest first)
|
|
1314
|
+
const scaleDesc = [...typeScale].reverse();
|
|
1315
|
+
|
|
1316
|
+
const sansFamily = typo.fontSans || typo.tailwindSans || typo.body || "system-ui, sans-serif";
|
|
1317
|
+
|
|
1318
|
+
const typoContent = [
|
|
1319
|
+
"import type { Meta, StoryObj } from \"@storybook/react\";",
|
|
1320
|
+
"",
|
|
1321
|
+
"const meta = { title: \"Foundations/Typography\" } satisfies Meta;",
|
|
1322
|
+
"export default meta;",
|
|
1323
|
+
"type Story = StoryObj;",
|
|
1324
|
+
"",
|
|
1325
|
+
`const typeScale: { step: string; size: string; px: string | null; lineHeight: string | null }[] = ${JSON.stringify(scaleDesc)};`,
|
|
1326
|
+
`const usedTextSizes: { token: string; count: number }[] = ${JSON.stringify(usedTextSizes)};`,
|
|
1327
|
+
`const familyRows: { token: string; value: string }[] = ${JSON.stringify(familyRows)};`,
|
|
1328
|
+
`const weightRows: { token: string; value: string }[] = ${JSON.stringify(weightRows)};`,
|
|
1329
|
+
`const sansFamily = ${JSON.stringify(sansFamily)};`,
|
|
1330
|
+
"",
|
|
1331
|
+
"export const Default: Story = {",
|
|
1332
|
+
" render: () => (",
|
|
1333
|
+
" <div style={{ fontFamily: \"system-ui,sans-serif\", padding: 32, maxWidth: 860, color: \"#111\" }}>",
|
|
1334
|
+
" <h2 style={{ fontSize: 20, fontWeight: 700, margin: \"0 0 4px\" }}>Typography</h2>",
|
|
1335
|
+
" <p style={{ fontSize: 13, color: \"#888\", margin: \"0 0 32px\" }}>Type scale, font families, and weights</p>",
|
|
1336
|
+
"",
|
|
1337
|
+
" {/* ── TYPE SCALE ── */}",
|
|
1338
|
+
" <h3 style={{ fontSize: 15, fontWeight: 600, margin: \"0 0 14px\", color: \"#374151\" }}>Type Scale</h3>",
|
|
1339
|
+
" {usedTextSizes.length > 0 && (",
|
|
1340
|
+
" <div style={{ marginBottom: 16 }}>",
|
|
1341
|
+
" <p style={{ fontSize: 12, color: \"#6b7280\", margin: \"0 0 8px\", fontWeight: 500 }}>Most used in this project</p>",
|
|
1342
|
+
" <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 6 }}>",
|
|
1343
|
+
" {usedTextSizes.map(({ token, count }) => (",
|
|
1344
|
+
" <span key={token} style={{ padding: \"3px 10px\", background: \"#ede9fe\", color: \"#5b21b6\", borderRadius: 20, fontSize: 12, fontWeight: 500 }}>",
|
|
1345
|
+
" {token} <span style={{ opacity: 0.6 }}>×{count}</span>",
|
|
1346
|
+
" </span>",
|
|
1347
|
+
" ))}",
|
|
1348
|
+
" </div>",
|
|
1349
|
+
" </div>",
|
|
1350
|
+
" )}",
|
|
1351
|
+
" {typeScale.length === 0 ? (",
|
|
1352
|
+
" <p style={{ color: \"#999\", fontSize: 13, marginBottom: 32 }}>No type scale detected.</p>",
|
|
1353
|
+
" ) : (",
|
|
1354
|
+
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 0, marginBottom: 48, border: \"1px solid #e5e7eb\", borderRadius: 10, overflow: \"hidden\" }}>",
|
|
1355
|
+
" {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);",
|
|
1359
|
+
" 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>}",
|
|
1365
|
+
" </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}",
|
|
1369
|
+
" </div>",
|
|
1370
|
+
" </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>",
|
|
1374
|
+
" </div>",
|
|
1375
|
+
" );",
|
|
1376
|
+
" })}",
|
|
1377
|
+
" </div>",
|
|
1378
|
+
" )}",
|
|
1379
|
+
"",
|
|
1380
|
+
" {/* ── FONT FAMILIES ── */}",
|
|
1381
|
+
" {familyRows.length > 0 && (",
|
|
1382
|
+
" <>",
|
|
1383
|
+
" <h3 style={{ fontSize: 15, fontWeight: 600, margin: \"0 0 14px\", color: \"#374151\" }}>Font Families</h3>",
|
|
1384
|
+
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 16, marginBottom: 40 }}>",
|
|
1385
|
+
" {familyRows.map(({ token, value }) => (",
|
|
1386
|
+
" <div key={token} style={{ padding: \"12px 16px\", background: \"#f8fafc\", borderRadius: 8, border: \"1px solid #e5e7eb\" }}>",
|
|
1387
|
+
" <div style={{ display: \"flex\", gap: 12, alignItems: \"baseline\", marginBottom: 6 }}>",
|
|
1388
|
+
" <span style={{ fontSize: 12, fontWeight: 600, color: \"#374151\", minWidth: 140 }}>{token}</span>",
|
|
1389
|
+
" <code style={{ fontSize: 11, color: \"#9ca3af\", wordBreak: \"break-all\" as any }}>{value.length > 60 ? value.slice(0, 60) + \"…\" : value}</code>",
|
|
1390
|
+
" </div>",
|
|
1391
|
+
" <div style={{ fontFamily: value, fontSize: 18, color: \"#374151\", letterSpacing: \"-0.01em\" }}>",
|
|
1392
|
+
" Aa — The quick brown fox jumps over the lazy dog.",
|
|
1393
|
+
" </div>",
|
|
1394
|
+
" </div>",
|
|
1395
|
+
" ))}",
|
|
1396
|
+
" </div>",
|
|
1397
|
+
" </>",
|
|
1398
|
+
" )}",
|
|
1399
|
+
"",
|
|
1400
|
+
" {/* ── FONT WEIGHTS ── */}",
|
|
1401
|
+
" {weightRows.length > 0 && (",
|
|
1402
|
+
" <>",
|
|
1403
|
+
" <h3 style={{ fontSize: 15, fontWeight: 600, margin: \"0 0 14px\", color: \"#374151\" }}>Font Weights</h3>",
|
|
1404
|
+
" <div style={{ display: \"flex\", flexDirection: \"column\", gap: 10, marginBottom: 40, maxWidth: 560 }}>",
|
|
1405
|
+
" {weightRows.map(({ token, value }) => (",
|
|
1406
|
+
" <div key={token} style={{ display: \"flex\", alignItems: \"baseline\", gap: 16, padding: \"8px 0\", borderBottom: \"1px solid #f0f0f0\" }}>",
|
|
1407
|
+
" <code style={{ width: 64, fontSize: 11, color: \"#9ca3af\", flexShrink: 0 }}>{value}</code>",
|
|
1408
|
+
" <span style={{ fontSize: 11, color: \"#9ca3af\", width: 120, flexShrink: 0 }}>{token}</span>",
|
|
1409
|
+
" <span style={{ fontFamily: sansFamily, fontSize: 17, fontWeight: parseInt(value) || value as any }}>",
|
|
1410
|
+
" The quick brown fox",
|
|
1411
|
+
" </span>",
|
|
1412
|
+
" </div>",
|
|
1413
|
+
" ))}",
|
|
1414
|
+
" </div>",
|
|
1415
|
+
" </>",
|
|
1416
|
+
" )}",
|
|
1417
|
+
" </div>",
|
|
1418
|
+
" ),",
|
|
1419
|
+
"};",
|
|
1420
|
+
].join("\n");
|
|
1325
1421
|
fs.writeFileSync(path.join(foundationsDir, "Typography.stories.tsx"), typoContent, "utf-8");
|
|
1326
1422
|
console.log("[VDS] Wrote " + path.relative(PROJECT_ROOT, path.join(foundationsDir, "Typography.stories.tsx")));
|
|
1327
1423
|
}
|
|
@@ -1576,7 +1672,8 @@ function writeFoundationsStories(foundations) {
|
|
|
1576
1672
|
"shadow-2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
|
|
1577
1673
|
};
|
|
1578
1674
|
// If token CSS values available use those; otherwise use defaults keyed by used token names
|
|
1579
|
-
const
|
|
1675
|
+
const hasCustomShadows = shadowRows.length > 0;
|
|
1676
|
+
const shadowDisplayRows = hasCustomShadows
|
|
1580
1677
|
? shadowRows
|
|
1581
1678
|
: usedShadows.map(({ token }) => ({ token, value: shadowDefaults[token] || "" })).filter((r) => r.value);
|
|
1582
1679
|
|
|
@@ -1594,6 +1691,7 @@ function writeFoundationsStories(foundations) {
|
|
|
1594
1691
|
`const usedShadows: { token: string; count: number }[] = ${JSON.stringify(usedShadows)};`,
|
|
1595
1692
|
`const usedZIndex: { token: string; count: number }[] = ${JSON.stringify(usedZIndex)};`,
|
|
1596
1693
|
`const zSemantics: Record<string, string> = ${JSON.stringify(zIndexSemantics)};`,
|
|
1694
|
+
`const hasCustomShadows: boolean = ${hasCustomShadows};`,
|
|
1597
1695
|
"",
|
|
1598
1696
|
"export const Default: Story = {",
|
|
1599
1697
|
" render: () => (",
|
|
@@ -1610,14 +1708,22 @@ function writeFoundationsStories(foundations) {
|
|
|
1610
1708
|
" ))}",
|
|
1611
1709
|
" </div>",
|
|
1612
1710
|
" )}",
|
|
1711
|
+
" {!hasCustomShadows && shadowTokens.length > 0 && (",
|
|
1712
|
+
" <p style={{ fontSize: 12, color: \"#92400e\", background: \"#fef3c7\", border: \"1px solid #fde68a\", borderRadius: 6, padding: \"6px 12px\", marginBottom: 16, display: \"inline-block\" }}>",
|
|
1713
|
+
" ℹ️ Tailwind built-in shadows — this project has no custom <code>--shadow-*</code> CSS vars",
|
|
1714
|
+
" </p>",
|
|
1715
|
+
" )}",
|
|
1613
1716
|
" {shadowTokens.length === 0 ? (",
|
|
1614
1717
|
" <p style={{ color: \"#999\", fontSize: 13, marginBottom: 32 }}>No shadow tokens detected.</p>",
|
|
1615
1718
|
" ) : (",
|
|
1616
|
-
" <div style={{ display: \"flex\", flexWrap: \"wrap\", gap:
|
|
1719
|
+
" <div style={{ display: \"flex\", flexWrap: \"wrap\", gap: 28, marginBottom: 48 }}>",
|
|
1617
1720
|
" {shadowTokens.map(({ token, value }) => (",
|
|
1618
|
-
" <div key={token} style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap:
|
|
1619
|
-
" <div style={{ width: 80, height:
|
|
1620
|
-
" <span style={{ fontSize: 12, fontWeight: 600, color: \"#374151\" }}>{token === \"DEFAULT\" ? \"default\" : token}</span>",
|
|
1721
|
+
" <div key={token} style={{ display: \"flex\", flexDirection: \"column\", alignItems: \"center\", gap: 8, maxWidth: 100 }}>",
|
|
1722
|
+
" <div style={{ width: 80, height: 72, background: \"#fff\", borderRadius: 10, boxShadow: value, border: token === \"none\" ? \"1px dashed #ccc\" : \"none\", flexShrink: 0 }} />",
|
|
1723
|
+
" <span style={{ fontSize: 12, fontWeight: 600, color: \"#374151\", textAlign: \"center\" }}>{token === \"DEFAULT\" ? \"default\" : token}</span>",
|
|
1724
|
+
" <code style={{ fontSize: 10, color: \"#9ca3af\", textAlign: \"center\", wordBreak: \"break-all\" as any, lineHeight: 1.4 }}>",
|
|
1725
|
+
" {value.length > 36 ? value.slice(0, 36) + \"…\" : value}",
|
|
1726
|
+
" </code>",
|
|
1621
1727
|
" </div>",
|
|
1622
1728
|
" ))}",
|
|
1623
1729
|
" </div>",
|