@loworbitstudio/visor-theme-engine 0.9.0 → 0.11.0
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/dist/adapters/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as GeneratedPrimitives, i as SemanticTokens, R as ResolvedThemeConfig } from '../types-
|
|
1
|
+
import { c as GeneratedPrimitives, i as SemanticTokens, R as ResolvedThemeConfig } from '../types-Dwc1V0Nc.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Adapter types for the Visor theme engine.
|
package/dist/adapters/index.js
CHANGED
|
@@ -6,14 +6,18 @@ import {
|
|
|
6
6
|
buildVisorFontUrl,
|
|
7
7
|
fontStack,
|
|
8
8
|
generateDarkCss,
|
|
9
|
+
generateHairlineDecls,
|
|
10
|
+
generateIntentDecls,
|
|
9
11
|
generateLightCss,
|
|
10
12
|
generatePrimitivesCss,
|
|
11
13
|
generateShadeScale,
|
|
14
|
+
generateSpaceAliasDecls,
|
|
15
|
+
generateTextScaleAliasDecls,
|
|
12
16
|
header,
|
|
13
17
|
parseColor,
|
|
14
18
|
resolveThemeFonts,
|
|
15
19
|
sectionComment
|
|
16
|
-
} from "../chunk-
|
|
20
|
+
} from "../chunk-43TVIXUS.js";
|
|
17
21
|
|
|
18
22
|
// src/adapters/layers.ts
|
|
19
23
|
var LAYER_ORDER = "@layer visor-primitives, visor-semantic, visor-adaptive, visor-bridge;";
|
|
@@ -589,9 +593,46 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
|
589
593
|
lines.push(sectionComment2("Fumadocs bridge: light"));
|
|
590
594
|
lines.push(block(`html:not(.dark) ${scopeClass}`, generateFumadocsBridgeDecls(input.tokens, "light")));
|
|
591
595
|
lines.push("");
|
|
592
|
-
const
|
|
596
|
+
const semanticLines = [];
|
|
597
|
+
semanticLines.push("\n/* \u2500\u2500 Layer: Semantic aliases (VI-451) \u2500\u2500 */");
|
|
598
|
+
semanticLines.push(sectionComment2("Discrete: Text size aliases (--text-N)"));
|
|
599
|
+
semanticLines.push(block(scopeClass, generateTextScaleAliasDecls()));
|
|
600
|
+
semanticLines.push("");
|
|
601
|
+
semanticLines.push(sectionComment2("Discrete: Space aliases (--space-N)"));
|
|
602
|
+
semanticLines.push(block(scopeClass, generateSpaceAliasDecls(input.config)));
|
|
603
|
+
semanticLines.push("");
|
|
604
|
+
semanticLines.push(sectionComment2("Intent aliases (light)"));
|
|
605
|
+
semanticLines.push(block(`html:not(.dark) ${scopeClass}`, generateIntentDecls(input.tokens, "light")));
|
|
606
|
+
semanticLines.push("");
|
|
607
|
+
semanticLines.push(sectionComment2("Hairline aliases (light)"));
|
|
608
|
+
semanticLines.push(block(`html:not(.dark) ${scopeClass}`, generateHairlineDecls(input.tokens, "light")));
|
|
609
|
+
semanticLines.push("");
|
|
610
|
+
semanticLines.push(sectionComment2("Intent aliases (dark) \u2014 manual toggle"));
|
|
611
|
+
semanticLines.push(block(`.dark ${scopeClass}`, generateIntentDecls(input.tokens, "dark")));
|
|
612
|
+
semanticLines.push("");
|
|
613
|
+
semanticLines.push(sectionComment2("Hairline aliases (dark) \u2014 manual toggle"));
|
|
614
|
+
semanticLines.push(block(`.dark ${scopeClass}`, generateHairlineDecls(input.tokens, "dark")));
|
|
615
|
+
semanticLines.push("");
|
|
616
|
+
semanticLines.push(sectionComment2("Intent aliases (dark) \u2014 prefers-color-scheme"));
|
|
617
|
+
{
|
|
618
|
+
const inner = block(`${scopeClass}:not(.light)`, generateIntentDecls(input.tokens, "dark"));
|
|
619
|
+
semanticLines.push(`@media (prefers-color-scheme: dark) {
|
|
620
|
+
${inner.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
621
|
+
}`);
|
|
622
|
+
}
|
|
623
|
+
semanticLines.push("");
|
|
624
|
+
semanticLines.push(sectionComment2("Hairline aliases (dark) \u2014 prefers-color-scheme"));
|
|
625
|
+
{
|
|
626
|
+
const inner = block(`${scopeClass}:not(.light)`, generateHairlineDecls(input.tokens, "dark"));
|
|
627
|
+
semanticLines.push(`@media (prefers-color-scheme: dark) {
|
|
628
|
+
${inner.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
629
|
+
}`);
|
|
630
|
+
}
|
|
631
|
+
semanticLines.push("");
|
|
632
|
+
const adaptiveLayer = wrapInLayer("visor-adaptive", lines.join("\n").trim());
|
|
633
|
+
const semanticLayer = wrapInLayer("visor-semantic", semanticLines.join("\n").trim());
|
|
593
634
|
const head = fontLines.length > 0 ? fontLines.join("\n") + "\n" : "";
|
|
594
|
-
return head + LAYER_ORDER + "\n\n" +
|
|
635
|
+
return head + LAYER_ORDER + "\n\n" + semanticLayer + "\n\n" + adaptiveLayer + "\n";
|
|
595
636
|
}
|
|
596
637
|
|
|
597
638
|
// src/flutter/color-to-dart.ts
|
|
@@ -1324,6 +1324,9 @@ function generateMotionPrimitives(config) {
|
|
|
1324
1324
|
decls.push(
|
|
1325
1325
|
"--motion-easing-spring: cubic-bezier(0.34, 1.56, 0.64, 1);"
|
|
1326
1326
|
);
|
|
1327
|
+
if (config.motion["easing-overshoot"]) {
|
|
1328
|
+
decls.push(`--motion-easing-overshoot: ${config.motion["easing-overshoot"]};`);
|
|
1329
|
+
}
|
|
1327
1330
|
return decls;
|
|
1328
1331
|
}
|
|
1329
1332
|
function generateMiscPrimitives() {
|
|
@@ -1369,6 +1372,9 @@ function generatePrimitivesCss(primitives, config, options) {
|
|
|
1369
1372
|
lines.push(block(host, generateMiscPrimitives()));
|
|
1370
1373
|
return header("Visor Theme \u2014 Primitives") + lines.join("\n");
|
|
1371
1374
|
}
|
|
1375
|
+
function hairlineDeclName(name) {
|
|
1376
|
+
return name === "default" ? "--hairline" : `--hairline-${name}`;
|
|
1377
|
+
}
|
|
1372
1378
|
function generateSemanticCss(tokens) {
|
|
1373
1379
|
const lines = [];
|
|
1374
1380
|
lines.push(sectionComment("Semantic: Text"));
|
|
@@ -1391,8 +1397,61 @@ function generateSemanticCss(tokens) {
|
|
|
1391
1397
|
([name, { light }]) => `--interactive-${name}: ${light};`
|
|
1392
1398
|
);
|
|
1393
1399
|
lines.push(block(":root", interactiveDecls));
|
|
1400
|
+
lines.push(sectionComment("Semantic: Intent (aliases)"));
|
|
1401
|
+
const intentDecls = Object.entries(tokens.intent).map(
|
|
1402
|
+
([name, { light }]) => `--${name}: ${light};`
|
|
1403
|
+
);
|
|
1404
|
+
lines.push(block(":root", intentDecls));
|
|
1405
|
+
lines.push(sectionComment("Semantic: Hairline (aliases)"));
|
|
1406
|
+
const hairlineDecls = Object.entries(tokens.hairline).map(
|
|
1407
|
+
([name, { light }]) => `${hairlineDeclName(name)}: ${light};`
|
|
1408
|
+
);
|
|
1409
|
+
lines.push(block(":root", hairlineDecls));
|
|
1394
1410
|
return header("Visor Theme \u2014 Semantic") + lines.join("\n");
|
|
1395
1411
|
}
|
|
1412
|
+
var TEXT_SCALE_ALIASES = [
|
|
1413
|
+
11,
|
|
1414
|
+
13,
|
|
1415
|
+
14,
|
|
1416
|
+
16,
|
|
1417
|
+
20,
|
|
1418
|
+
24,
|
|
1419
|
+
32,
|
|
1420
|
+
40,
|
|
1421
|
+
48
|
|
1422
|
+
];
|
|
1423
|
+
function generateTextScaleAliasDecls() {
|
|
1424
|
+
return TEXT_SCALE_ALIASES.map((px) => `--text-${px}: ${px}px;`);
|
|
1425
|
+
}
|
|
1426
|
+
var SPACE_ALIAS_MULTIPLIERS = [
|
|
1427
|
+
1,
|
|
1428
|
+
2,
|
|
1429
|
+
3,
|
|
1430
|
+
4,
|
|
1431
|
+
5,
|
|
1432
|
+
6,
|
|
1433
|
+
8,
|
|
1434
|
+
10,
|
|
1435
|
+
12,
|
|
1436
|
+
16
|
|
1437
|
+
];
|
|
1438
|
+
function generateSpaceAliasDecls(config) {
|
|
1439
|
+
const base = config.spacing.base;
|
|
1440
|
+
return SPACE_ALIAS_MULTIPLIERS.map((m) => {
|
|
1441
|
+
const px = base * m;
|
|
1442
|
+
return `--space-${m}: ${px}px;`;
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
function generateIntentDecls(tokens, mode) {
|
|
1446
|
+
return Object.entries(tokens.intent).map(
|
|
1447
|
+
([name, value]) => `--${name}: ${value[mode]};`
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
function generateHairlineDecls(tokens, mode) {
|
|
1451
|
+
return Object.entries(tokens.hairline).map(
|
|
1452
|
+
([name, value]) => `${hairlineDeclName(name)}: ${value[mode]};`
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1396
1455
|
function buildAdaptiveDecls(tokens, theme) {
|
|
1397
1456
|
const textDecls = Object.entries(tokens.text).map(
|
|
1398
1457
|
([name, values]) => `--text-${name}: ${values[theme]};`
|
|
@@ -1406,11 +1465,13 @@ function buildAdaptiveDecls(tokens, theme) {
|
|
|
1406
1465
|
const interactiveDecls = Object.entries(tokens.interactive).map(
|
|
1407
1466
|
([name, values]) => `--interactive-${name}: ${values[theme]};`
|
|
1408
1467
|
);
|
|
1409
|
-
|
|
1468
|
+
const intentDecls = generateIntentDecls(tokens, theme);
|
|
1469
|
+
const hairlineDecls = generateHairlineDecls(tokens, theme);
|
|
1470
|
+
return { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls };
|
|
1410
1471
|
}
|
|
1411
1472
|
function generateLightCss(tokens, options) {
|
|
1412
1473
|
const lines = [];
|
|
1413
|
-
const { textDecls, surfaceDecls, borderDecls, interactiveDecls } = buildAdaptiveDecls(tokens, "light");
|
|
1474
|
+
const { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls } = buildAdaptiveDecls(tokens, "light");
|
|
1414
1475
|
const host = options?.scopePrefix ?? ":root";
|
|
1415
1476
|
lines.push(sectionComment("Adaptive: Text (light)"));
|
|
1416
1477
|
lines.push(block(host, textDecls));
|
|
@@ -1420,11 +1481,15 @@ function generateLightCss(tokens, options) {
|
|
|
1420
1481
|
lines.push(block(host, borderDecls));
|
|
1421
1482
|
lines.push(sectionComment("Adaptive: Interactive (light)"));
|
|
1422
1483
|
lines.push(block(host, interactiveDecls));
|
|
1484
|
+
lines.push(sectionComment("Adaptive: Intent aliases (light)"));
|
|
1485
|
+
lines.push(block(host, intentDecls));
|
|
1486
|
+
lines.push(sectionComment("Adaptive: Hairline aliases (light)"));
|
|
1487
|
+
lines.push(block(host, hairlineDecls));
|
|
1423
1488
|
return header("Visor Theme \u2014 Light") + lines.join("\n");
|
|
1424
1489
|
}
|
|
1425
1490
|
function generateDarkCss(tokens, options) {
|
|
1426
1491
|
const lines = [];
|
|
1427
|
-
const { textDecls, surfaceDecls, borderDecls, interactiveDecls } = buildAdaptiveDecls(tokens, "dark");
|
|
1492
|
+
const { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls } = buildAdaptiveDecls(tokens, "dark");
|
|
1428
1493
|
const prefix = options?.scopePrefix;
|
|
1429
1494
|
const darkSelectors = prefix ? [`${prefix}.dark`, `${prefix}.theme-dark`, `${prefix}[data-theme="dark"]`] : [".dark", ".theme-dark", '[data-theme="dark"]'];
|
|
1430
1495
|
const darkSelector = darkSelectors.join(",\n");
|
|
@@ -1437,6 +1502,10 @@ function generateDarkCss(tokens, options) {
|
|
|
1437
1502
|
lines.push(block(darkSelector, borderDecls));
|
|
1438
1503
|
lines.push(sectionComment("Adaptive: Interactive (dark) \u2014 manual toggle"));
|
|
1439
1504
|
lines.push(block(darkSelector, interactiveDecls));
|
|
1505
|
+
lines.push(sectionComment("Adaptive: Intent aliases (dark) \u2014 manual toggle"));
|
|
1506
|
+
lines.push(block(darkSelector, intentDecls));
|
|
1507
|
+
lines.push(sectionComment("Adaptive: Hairline aliases (dark) \u2014 manual toggle"));
|
|
1508
|
+
lines.push(block(darkSelector, hairlineDecls));
|
|
1440
1509
|
lines.push(
|
|
1441
1510
|
sectionComment("Adaptive: Text (dark) \u2014 prefers-color-scheme")
|
|
1442
1511
|
);
|
|
@@ -1467,6 +1536,22 @@ ${block(prefersSelector, borderDecls)}}`
|
|
|
1467
1536
|
lines.push(
|
|
1468
1537
|
`@media (prefers-color-scheme: dark) {
|
|
1469
1538
|
${block(prefersSelector, interactiveDecls)}}`
|
|
1539
|
+
);
|
|
1540
|
+
lines.push("");
|
|
1541
|
+
lines.push(
|
|
1542
|
+
sectionComment("Adaptive: Intent aliases (dark) \u2014 prefers-color-scheme")
|
|
1543
|
+
);
|
|
1544
|
+
lines.push(
|
|
1545
|
+
`@media (prefers-color-scheme: dark) {
|
|
1546
|
+
${block(prefersSelector, intentDecls)}}`
|
|
1547
|
+
);
|
|
1548
|
+
lines.push("");
|
|
1549
|
+
lines.push(
|
|
1550
|
+
sectionComment("Adaptive: Hairline aliases (dark) \u2014 prefers-color-scheme")
|
|
1551
|
+
);
|
|
1552
|
+
lines.push(
|
|
1553
|
+
`@media (prefers-color-scheme: dark) {
|
|
1554
|
+
${block(prefersSelector, hairlineDecls)}}`
|
|
1470
1555
|
);
|
|
1471
1556
|
lines.push("");
|
|
1472
1557
|
return header("Visor Theme \u2014 Dark") + lines.join("\n");
|
|
@@ -1561,6 +1646,10 @@ export {
|
|
|
1561
1646
|
sectionComment,
|
|
1562
1647
|
generatePrimitivesCss,
|
|
1563
1648
|
generateSemanticCss,
|
|
1649
|
+
generateTextScaleAliasDecls,
|
|
1650
|
+
generateSpaceAliasDecls,
|
|
1651
|
+
generateIntentDecls,
|
|
1652
|
+
generateHairlineDecls,
|
|
1564
1653
|
generateLightCss,
|
|
1565
1654
|
generateDarkCss,
|
|
1566
1655
|
generateFullBundleCss
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, R as ResolvedThemeConfig, c as GeneratedPrimitives, d as ThemeOutput, e as ThemeData, f as VisorThemeConfig, g as FullShadeScale, C as ColorRole, S as SelectiveShadeScale, h as RGB, P as ParsedColor, O as OKLCH, i as SemanticTokens, j as ShadeStep } from './types-
|
|
2
|
-
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-
|
|
1
|
+
import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, R as ResolvedThemeConfig, c as GeneratedPrimitives, d as ThemeOutput, e as ThemeData, f as VisorThemeConfig, g as FullShadeScale, C as ColorRole, S as SelectiveShadeScale, h as RGB, P as ParsedColor, O as OKLCH, i as SemanticTokens, j as ShadeStep } from './types-Dwc1V0Nc.js';
|
|
2
|
+
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-Dwc1V0Nc.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Font resolver — maps font family names to loadable font resources.
|
|
@@ -898,6 +898,8 @@ declare const SEMANTIC_MAP: {
|
|
|
898
898
|
surface: Record<string, SemanticMapping>;
|
|
899
899
|
border: Record<string, SemanticMapping>;
|
|
900
900
|
interactive: Record<string, SemanticMapping>;
|
|
901
|
+
intent: Record<string, SemanticMapping>;
|
|
902
|
+
hairline: Record<string, SemanticMapping>;
|
|
901
903
|
};
|
|
902
904
|
|
|
903
905
|
/**
|
package/dist/index.js
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
rgbToHex,
|
|
35
35
|
rgbToOklch,
|
|
36
36
|
serializeColor
|
|
37
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-43TVIXUS.js";
|
|
38
38
|
|
|
39
39
|
// src/fonts/validate-coverage.ts
|
|
40
40
|
var FONT_VAR_RE = /--font-(heading|display|body|sans|mono)\s*:\s*([^;]+);/g;
|
|
@@ -605,7 +605,7 @@ var KNOWN_SPACING_KEYS = /* @__PURE__ */ new Set(["base"]);
|
|
|
605
605
|
var KNOWN_RADIUS_KEYS = /* @__PURE__ */ new Set(["sm", "md", "lg", "xl", "pill"]);
|
|
606
606
|
var KNOWN_SHADOW_KEYS = /* @__PURE__ */ new Set(["xs", "sm", "md", "lg", "xl"]);
|
|
607
607
|
var KNOWN_STROKE_WIDTH_KEYS = /* @__PURE__ */ new Set(["thin", "regular", "medium", "thick"]);
|
|
608
|
-
var KNOWN_MOTION_KEYS = /* @__PURE__ */ new Set(["duration-fast", "duration-normal", "duration-slow", "easing"]);
|
|
608
|
+
var KNOWN_MOTION_KEYS = /* @__PURE__ */ new Set(["duration-fast", "duration-normal", "duration-slow", "easing", "easing-overshoot"]);
|
|
609
609
|
var KNOWN_OVERRIDES_KEYS = /* @__PURE__ */ new Set(["light", "dark"]);
|
|
610
610
|
function checkUnknownKeys(obj, errors) {
|
|
611
611
|
for (const key of Object.keys(obj)) {
|
|
@@ -1024,7 +1024,10 @@ function resolveConfig(config) {
|
|
|
1024
1024
|
"duration-fast": config.motion?.["duration-fast"] ?? DEFAULTS.motion["duration-fast"],
|
|
1025
1025
|
"duration-normal": config.motion?.["duration-normal"] ?? DEFAULTS.motion["duration-normal"],
|
|
1026
1026
|
"duration-slow": config.motion?.["duration-slow"] ?? DEFAULTS.motion["duration-slow"],
|
|
1027
|
-
easing: config.motion?.easing ?? DEFAULTS.motion.easing
|
|
1027
|
+
easing: config.motion?.easing ?? DEFAULTS.motion.easing,
|
|
1028
|
+
// VI-451 (drive-by): pass through opt-in bouncy easing when set; absent
|
|
1029
|
+
// themes leave this undefined and fall through to --motion-easing-spring.
|
|
1030
|
+
...config.motion?.["easing-overshoot"] !== void 0 ? { "easing-overshoot": config.motion["easing-overshoot"] } : {}
|
|
1028
1031
|
},
|
|
1029
1032
|
overrides: config.overrides,
|
|
1030
1033
|
originalColors,
|
|
@@ -1057,6 +1060,11 @@ var SEMANTIC_TEXT_MAP = {
|
|
|
1057
1060
|
light: { role: "neutral", shade: 600 },
|
|
1058
1061
|
dark: { role: "neutral", shade: 400 }
|
|
1059
1062
|
},
|
|
1063
|
+
// VI-451: deemphasized but readable — slots between tertiary and disabled.
|
|
1064
|
+
muted: {
|
|
1065
|
+
light: { role: "neutral", shade: 500 },
|
|
1066
|
+
dark: { role: "neutral", shade: 500 }
|
|
1067
|
+
},
|
|
1060
1068
|
disabled: {
|
|
1061
1069
|
light: { role: "neutral", shade: 300 },
|
|
1062
1070
|
dark: { role: "neutral", shade: 600 }
|
|
@@ -1117,6 +1125,16 @@ var SEMANTIC_SURFACE_MAP = {
|
|
|
1117
1125
|
light: { role: "neutral", shade: 100 },
|
|
1118
1126
|
dark: { role: "neutral", shade: 700 }
|
|
1119
1127
|
},
|
|
1128
|
+
// VI-451: deeper than page — used by admin-ui chrome (deepest backdrop).
|
|
1129
|
+
screen: {
|
|
1130
|
+
light: { role: "neutral", shade: 50 },
|
|
1131
|
+
dark: { role: "neutral", shade: 950 }
|
|
1132
|
+
},
|
|
1133
|
+
// VI-451: singular elevation alias, distinct from elev-0..4. Defaults to mid (elev-2).
|
|
1134
|
+
elev: {
|
|
1135
|
+
light: { role: "neutral", shade: 100 },
|
|
1136
|
+
dark: { role: "neutral", shade: 800 }
|
|
1137
|
+
},
|
|
1120
1138
|
overlay: {
|
|
1121
1139
|
light: { role: "neutral", shade: 900 },
|
|
1122
1140
|
dark: { role: "neutral", shade: 950 }
|
|
@@ -1187,6 +1205,23 @@ var SEMANTIC_SURFACE_MAP = {
|
|
|
1187
1205
|
light: { role: "info", shade: 500 },
|
|
1188
1206
|
dark: { role: "info", shade: 500 }
|
|
1189
1207
|
},
|
|
1208
|
+
// VI-478: status soft tints (BL-193) — alpha overlays, semantically distinct
|
|
1209
|
+
// from the OPAQUE `surface-{status}-subtle` above (do NOT alias them together).
|
|
1210
|
+
// Default to a color-mix of the status color so they track the theme; themes
|
|
1211
|
+
// pin exact values via overrides (blacklight-underground: success @10%,
|
|
1212
|
+
// warning/error @12%).
|
|
1213
|
+
"success-soft": {
|
|
1214
|
+
light: { constant: "color-mix(in srgb, var(--color-success-500) 10%, transparent)" },
|
|
1215
|
+
dark: { constant: "color-mix(in srgb, var(--color-success-500) 10%, transparent)" }
|
|
1216
|
+
},
|
|
1217
|
+
"warning-soft": {
|
|
1218
|
+
light: { constant: "color-mix(in srgb, var(--color-warning-500) 12%, transparent)" },
|
|
1219
|
+
dark: { constant: "color-mix(in srgb, var(--color-warning-500) 12%, transparent)" }
|
|
1220
|
+
},
|
|
1221
|
+
"error-soft": {
|
|
1222
|
+
light: { constant: "color-mix(in srgb, var(--color-error-500) 12%, transparent)" },
|
|
1223
|
+
dark: { constant: "color-mix(in srgb, var(--color-error-500) 12%, transparent)" }
|
|
1224
|
+
},
|
|
1190
1225
|
// 5-tier ordinal elevation scale — deepest (0) to highest (4)
|
|
1191
1226
|
// Light mode: BO-10 near-white ramp (white → neutral-300).
|
|
1192
1227
|
// Dark mode: deep neutral ramp (neutral-950 → neutral-600).
|
|
@@ -1267,6 +1302,23 @@ var SEMANTIC_INTERACTIVE_MAP = {
|
|
|
1267
1302
|
light: { constant: "#ffffff" },
|
|
1268
1303
|
dark: { constant: "#ffffff" }
|
|
1269
1304
|
},
|
|
1305
|
+
// VI-478: brand-derived alpha-overlay helpers (BL-193). `soft`/`glow` are
|
|
1306
|
+
// alpha overlays that track the theme's primary via color-mix (distinct from
|
|
1307
|
+
// any opaque surface); `strong` is a solid lightened-brand emphasis color.
|
|
1308
|
+
// Themes pin exact values via overrides — e.g. blacklight-underground sets
|
|
1309
|
+
// soft @12% / glow @32% / strong #FFD050.
|
|
1310
|
+
"primary-soft": {
|
|
1311
|
+
light: { constant: "color-mix(in srgb, var(--color-primary-500) 12%, transparent)" },
|
|
1312
|
+
dark: { constant: "color-mix(in srgb, var(--color-primary-500) 12%, transparent)" }
|
|
1313
|
+
},
|
|
1314
|
+
"primary-glow": {
|
|
1315
|
+
light: { constant: "color-mix(in srgb, var(--color-primary-500) 32%, transparent)" },
|
|
1316
|
+
dark: { constant: "color-mix(in srgb, var(--color-primary-500) 32%, transparent)" }
|
|
1317
|
+
},
|
|
1318
|
+
"primary-strong": {
|
|
1319
|
+
light: { role: "primary", shade: 600 },
|
|
1320
|
+
dark: { role: "primary", shade: 400 }
|
|
1321
|
+
},
|
|
1270
1322
|
// Secondary action
|
|
1271
1323
|
"secondary-bg": {
|
|
1272
1324
|
light: { constant: "#ffffff" },
|
|
@@ -1311,11 +1363,56 @@ var SEMANTIC_INTERACTIVE_MAP = {
|
|
|
1311
1363
|
dark: { role: "neutral", shade: 700 }
|
|
1312
1364
|
}
|
|
1313
1365
|
};
|
|
1366
|
+
var SEMANTIC_INTENT_MAP = {
|
|
1367
|
+
primary: {
|
|
1368
|
+
light: { role: "primary", shade: 500 },
|
|
1369
|
+
dark: { role: "primary", shade: 500 }
|
|
1370
|
+
},
|
|
1371
|
+
// Text color paired with --primary backgrounds. Default white; themes whose
|
|
1372
|
+
// primary fails AA on white pin to a graphite via overrides (entr does this).
|
|
1373
|
+
"primary-text": {
|
|
1374
|
+
light: { constant: "#ffffff" },
|
|
1375
|
+
dark: { constant: "#ffffff" }
|
|
1376
|
+
},
|
|
1377
|
+
accent: {
|
|
1378
|
+
light: { role: "accent", shade: 500 },
|
|
1379
|
+
dark: { role: "accent", shade: 500 }
|
|
1380
|
+
},
|
|
1381
|
+
success: {
|
|
1382
|
+
light: { role: "success", shade: 500 },
|
|
1383
|
+
dark: { role: "success", shade: 500 }
|
|
1384
|
+
},
|
|
1385
|
+
warning: {
|
|
1386
|
+
light: { role: "warning", shade: 500 },
|
|
1387
|
+
dark: { role: "warning", shade: 500 }
|
|
1388
|
+
},
|
|
1389
|
+
// shadcn naming — aliases the `error` role.
|
|
1390
|
+
destructive: {
|
|
1391
|
+
light: { role: "error", shade: 500 },
|
|
1392
|
+
dark: { role: "error", shade: 500 }
|
|
1393
|
+
},
|
|
1394
|
+
info: {
|
|
1395
|
+
light: { role: "info", shade: 500 },
|
|
1396
|
+
dark: { role: "info", shade: 500 }
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
var SEMANTIC_HAIRLINE_MAP = {
|
|
1400
|
+
default: {
|
|
1401
|
+
light: { constant: "rgba(0, 0, 0, 0.06)" },
|
|
1402
|
+
dark: { constant: "rgba(255, 255, 255, 0.06)" }
|
|
1403
|
+
},
|
|
1404
|
+
strong: {
|
|
1405
|
+
light: { constant: "rgba(0, 0, 0, 0.10)" },
|
|
1406
|
+
dark: { constant: "rgba(255, 255, 255, 0.10)" }
|
|
1407
|
+
}
|
|
1408
|
+
};
|
|
1314
1409
|
var SEMANTIC_MAP = {
|
|
1315
1410
|
text: SEMANTIC_TEXT_MAP,
|
|
1316
1411
|
surface: SEMANTIC_SURFACE_MAP,
|
|
1317
1412
|
border: SEMANTIC_BORDER_MAP,
|
|
1318
|
-
interactive: SEMANTIC_INTERACTIVE_MAP
|
|
1413
|
+
interactive: SEMANTIC_INTERACTIVE_MAP,
|
|
1414
|
+
intent: SEMANTIC_INTENT_MAP,
|
|
1415
|
+
hairline: SEMANTIC_HAIRLINE_MAP
|
|
1319
1416
|
};
|
|
1320
1417
|
|
|
1321
1418
|
// src/assign.ts
|
|
@@ -1357,6 +1454,8 @@ function assignSemanticTokens(lightPrimitives, darkPrimitives, config) {
|
|
|
1357
1454
|
const surface = {};
|
|
1358
1455
|
const border = {};
|
|
1359
1456
|
const interactive = {};
|
|
1457
|
+
const intent = {};
|
|
1458
|
+
const hairline = {};
|
|
1360
1459
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.text)) {
|
|
1361
1460
|
text[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
1362
1461
|
}
|
|
@@ -1369,7 +1468,13 @@ function assignSemanticTokens(lightPrimitives, darkPrimitives, config) {
|
|
|
1369
1468
|
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.interactive)) {
|
|
1370
1469
|
interactive[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
1371
1470
|
}
|
|
1372
|
-
|
|
1471
|
+
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.intent)) {
|
|
1472
|
+
intent[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
1473
|
+
}
|
|
1474
|
+
for (const [name, mapping] of Object.entries(SEMANTIC_MAP.hairline)) {
|
|
1475
|
+
hairline[name] = resolveMapping(mapping, lightPrimitives, darkPrimitives, config);
|
|
1476
|
+
}
|
|
1477
|
+
return { text, surface, border, interactive, intent, hairline };
|
|
1373
1478
|
}
|
|
1374
1479
|
|
|
1375
1480
|
// src/overrides.ts
|
|
@@ -1377,9 +1482,13 @@ var TOKEN_CATEGORIES = [
|
|
|
1377
1482
|
{ prefix: "text-", key: "text" },
|
|
1378
1483
|
{ prefix: "surface-", key: "surface" },
|
|
1379
1484
|
{ prefix: "border-", key: "border" },
|
|
1380
|
-
{ prefix: "interactive-", key: "interactive" }
|
|
1485
|
+
{ prefix: "interactive-", key: "interactive" },
|
|
1486
|
+
{ prefix: "hairline-", key: "hairline" }
|
|
1381
1487
|
];
|
|
1382
1488
|
function findToken(key, tokens) {
|
|
1489
|
+
if (key === "hairline" && "default" in tokens.hairline) {
|
|
1490
|
+
return { group: tokens.hairline, name: "default" };
|
|
1491
|
+
}
|
|
1383
1492
|
for (const { prefix, key: groupKey } of TOKEN_CATEGORIES) {
|
|
1384
1493
|
if (key.startsWith(prefix)) {
|
|
1385
1494
|
const name = key.slice(prefix.length);
|
|
@@ -1388,6 +1497,9 @@ function findToken(key, tokens) {
|
|
|
1388
1497
|
}
|
|
1389
1498
|
}
|
|
1390
1499
|
}
|
|
1500
|
+
if (key in tokens.intent) {
|
|
1501
|
+
return { group: tokens.intent, name: key };
|
|
1502
|
+
}
|
|
1391
1503
|
return null;
|
|
1392
1504
|
}
|
|
1393
1505
|
function applyOverrides(tokens, overrides) {
|
|
@@ -1396,9 +1508,11 @@ function applyOverrides(tokens, overrides) {
|
|
|
1396
1508
|
text: { ...tokens.text },
|
|
1397
1509
|
surface: { ...tokens.surface },
|
|
1398
1510
|
border: { ...tokens.border },
|
|
1399
|
-
interactive: { ...tokens.interactive }
|
|
1511
|
+
interactive: { ...tokens.interactive },
|
|
1512
|
+
intent: { ...tokens.intent },
|
|
1513
|
+
hairline: { ...tokens.hairline }
|
|
1400
1514
|
};
|
|
1401
|
-
for (const group of ["text", "surface", "border", "interactive"]) {
|
|
1515
|
+
for (const group of ["text", "surface", "border", "interactive", "intent", "hairline"]) {
|
|
1402
1516
|
for (const [name, value] of Object.entries(result[group])) {
|
|
1403
1517
|
result[group][name] = { ...value };
|
|
1404
1518
|
}
|
|
@@ -1615,10 +1729,13 @@ function exportTheme(primitives, config) {
|
|
|
1615
1729
|
const motion = {};
|
|
1616
1730
|
for (const [key, defaultVal] of Object.entries(DEFAULT_MOTION)) {
|
|
1617
1731
|
const val = config.motion[key];
|
|
1618
|
-
if (val !== defaultVal) {
|
|
1732
|
+
if (val !== void 0 && val !== defaultVal) {
|
|
1619
1733
|
motion[key] = val;
|
|
1620
1734
|
}
|
|
1621
1735
|
}
|
|
1736
|
+
if (config.motion["easing-overshoot"] !== void 0) {
|
|
1737
|
+
motion["easing-overshoot"] = config.motion["easing-overshoot"];
|
|
1738
|
+
}
|
|
1622
1739
|
if (Object.keys(motion).length > 0) {
|
|
1623
1740
|
output.motion = motion;
|
|
1624
1741
|
}
|
|
@@ -268,6 +268,12 @@ interface VisorThemeConfig {
|
|
|
268
268
|
"duration-normal"?: string;
|
|
269
269
|
"duration-slow"?: string;
|
|
270
270
|
easing?: string;
|
|
271
|
+
/**
|
|
272
|
+
* VI-451 (drive-by): opt-in tier-2 easing for bouncy entrances
|
|
273
|
+
* (marker pops, scale-in entrances). Emitted as `--motion-easing-overshoot`
|
|
274
|
+
* when set; absent themes fall through to the default `--motion-easing-spring`.
|
|
275
|
+
*/
|
|
276
|
+
"easing-overshoot"?: string;
|
|
271
277
|
};
|
|
272
278
|
overrides?: {
|
|
273
279
|
light?: Record<string, string>;
|
|
@@ -380,6 +386,8 @@ interface ResolvedThemeConfig {
|
|
|
380
386
|
"duration-normal": string;
|
|
381
387
|
"duration-slow": string;
|
|
382
388
|
easing: string;
|
|
389
|
+
/** VI-451 (drive-by): opt-in bouncy easing for marker pops / scale-in entrances. */
|
|
390
|
+
"easing-overshoot"?: string;
|
|
383
391
|
};
|
|
384
392
|
overrides?: {
|
|
385
393
|
light?: Record<string, string>;
|
|
@@ -408,6 +416,8 @@ interface SemanticTokens {
|
|
|
408
416
|
surface: Record<string, SemanticTokenValue>;
|
|
409
417
|
border: Record<string, SemanticTokenValue>;
|
|
410
418
|
interactive: Record<string, SemanticTokenValue>;
|
|
419
|
+
intent: Record<string, SemanticTokenValue>;
|
|
420
|
+
hairline: Record<string, SemanticTokenValue>;
|
|
411
421
|
}
|
|
412
422
|
interface ThemeOutput {
|
|
413
423
|
primitivesCss: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loworbitstudio/visor-theme-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Theme engine for the Visor design system — shade generation, token mapping, font resolution, and import/export for .visor.yaml themes.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|