@loworbitstudio/visor-theme-engine 0.1.0 → 0.4.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/README.md +29 -0
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +33 -1
- package/dist/{chunk-ZLXFCNYF.js → chunk-NZF2MS4L.js} +30 -23
- package/dist/index.d.ts +2 -2
- package/dist/index.js +70 -5
- package/dist/{types-r7ae3WP2.d.ts → types-DgAumoCX.d.ts} +16 -0
- package/package.json +7 -2
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @loworbitstudio/visor-theme-engine
|
|
2
|
+
|
|
3
|
+
Theme engine for the [Visor](https://visor.loworbit.studio) design system — shade generation, token mapping, font resolution, and import/export for `.visor.yaml` themes.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @loworbitstudio/visor-theme-engine
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What It Does
|
|
12
|
+
|
|
13
|
+
- Generates dark and light color scales from a brand anchor color using OKLCH
|
|
14
|
+
- Maps theme configuration (`.visor.yaml`) to CSS custom properties
|
|
15
|
+
- Resolves font declarations against the Visor fonts CDN
|
|
16
|
+
- Exports theme bundles for use in any project
|
|
17
|
+
- Provides a `docsAdapter` for registering themes in fumadocs sites
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Themes are typically managed via the Visor CLI (`visor theme sync`). Direct API usage is for advanced cases — building custom theme tooling or integrating with non-CLI workflows.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { generateTheme } from '@loworbitstudio/visor-theme-engine'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Documentation
|
|
28
|
+
|
|
29
|
+
Full docs at [visor.loworbit.studio](https://visor.loworbit.studio).
|
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-DgAumoCX.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Adapter types for the Visor theme engine.
|
package/dist/adapters/index.js
CHANGED
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
generateDarkCss,
|
|
6
6
|
generateLightCss,
|
|
7
7
|
generatePrimitivesCss,
|
|
8
|
+
generateShadeScale,
|
|
8
9
|
header,
|
|
9
10
|
resolveThemeFonts,
|
|
10
11
|
sectionComment
|
|
11
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-NZF2MS4L.js";
|
|
12
13
|
|
|
13
14
|
// src/adapters/layers.ts
|
|
14
15
|
var LAYER_ORDER = "@layer visor-primitives, visor-semantic, visor-adaptive, visor-bridge;";
|
|
@@ -427,6 +428,7 @@ function docsAdapter(input, options) {
|
|
|
427
428
|
lines.push("");
|
|
428
429
|
}
|
|
429
430
|
}
|
|
431
|
+
const scale = input.config.typography?.scale ?? 1;
|
|
430
432
|
const seenFamilies = /* @__PURE__ */ new Set();
|
|
431
433
|
for (const font of fontSlots) {
|
|
432
434
|
if (font && font.source === "visor-fonts" && !seenFamilies.has(font.family)) {
|
|
@@ -439,6 +441,9 @@ function docsAdapter(input, options) {
|
|
|
439
441
|
lines.push(` font-weight: ${weight};`);
|
|
440
442
|
lines.push(` font-style: ${font.italic ? "italic" : "normal"};`);
|
|
441
443
|
lines.push(` font-display: ${font.display};`);
|
|
444
|
+
if (scale !== 1) {
|
|
445
|
+
lines.push(` size-adjust: ${Math.round(scale * 100)}%;`);
|
|
446
|
+
}
|
|
442
447
|
lines.push("}");
|
|
443
448
|
lines.push("");
|
|
444
449
|
}
|
|
@@ -480,6 +485,25 @@ function docsAdapter(input, options) {
|
|
|
480
485
|
lines.push("");
|
|
481
486
|
lines.push("\n/* \u2500\u2500 Section 2: Dark mode overrides \u2500\u2500 */");
|
|
482
487
|
const darkDecls = generateSemanticDecls2(input.tokens, "dark");
|
|
488
|
+
const colorsDark = input.config["colors-dark"];
|
|
489
|
+
const darkPrimitiveOverrides = [];
|
|
490
|
+
if (colorsDark?.primary) {
|
|
491
|
+
const darkPrimary = generateShadeScale(colorsDark.primary, "primary");
|
|
492
|
+
for (const step of FULL_SHADE_STEPS) {
|
|
493
|
+
darkPrimitiveOverrides.push(`--color-primary-${step}: ${darkPrimary[step]};`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
if (colorsDark?.accent) {
|
|
497
|
+
const darkAccent = generateShadeScale(colorsDark.accent, "accent");
|
|
498
|
+
for (const step of FULL_SHADE_STEPS) {
|
|
499
|
+
darkPrimitiveOverrides.push(`--color-accent-${step}: ${darkAccent[step]};`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
if (darkPrimitiveOverrides.length > 0) {
|
|
503
|
+
lines.push(sectionComment2("Primitive overrides (dark) \u2014 dark brand color anchors at shade 500"));
|
|
504
|
+
lines.push(block(`.dark ${scopeClass}`, darkPrimitiveOverrides));
|
|
505
|
+
lines.push("");
|
|
506
|
+
}
|
|
483
507
|
const categories = ["Text", "Surface", "Border", "Interactive"];
|
|
484
508
|
const categoryDecls = [
|
|
485
509
|
Object.entries(input.tokens.text).map(([n, v]) => `--text-${n}: ${v.dark};`),
|
|
@@ -503,6 +527,14 @@ function docsAdapter(input, options) {
|
|
|
503
527
|
const inner = block(`${scopeClass}:not(.light)`, cat.entries);
|
|
504
528
|
lines.push(`@media (prefers-color-scheme: dark) {
|
|
505
529
|
${inner.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
530
|
+
}`);
|
|
531
|
+
lines.push("");
|
|
532
|
+
}
|
|
533
|
+
if (darkPrimitiveOverrides.length > 0) {
|
|
534
|
+
lines.push(sectionComment2("Primitive overrides (dark) \u2014 prefers-color-scheme"));
|
|
535
|
+
const inner = block(`${scopeClass}:not(.light)`, darkPrimitiveOverrides);
|
|
536
|
+
lines.push(`@media (prefers-color-scheme: dark) {
|
|
537
|
+
${inner.split("\n").map((l) => ` ${l}`).join("\n")}
|
|
506
538
|
}`);
|
|
507
539
|
lines.push("");
|
|
508
540
|
}
|
|
@@ -467,8 +467,7 @@ function resolveThemeFonts(typography, options) {
|
|
|
467
467
|
const warnings = [];
|
|
468
468
|
let headingResolution = null;
|
|
469
469
|
if (typography.heading?.family) {
|
|
470
|
-
const weights = [];
|
|
471
|
-
if (typography.heading.weight) weights.push(typography.heading.weight);
|
|
470
|
+
const weights = typography.heading.weights ? [...typography.heading.weights] : typography.heading.weight ? [typography.heading.weight] : [];
|
|
472
471
|
headingResolution = resolveFont(typography.heading.family, {
|
|
473
472
|
weights: weights.length > 0 ? weights : void 0,
|
|
474
473
|
display,
|
|
@@ -481,10 +480,15 @@ function resolveThemeFonts(typography, options) {
|
|
|
481
480
|
}
|
|
482
481
|
let bodyResolution = null;
|
|
483
482
|
if (typography.body?.family) {
|
|
484
|
-
|
|
485
|
-
if (typography.body.
|
|
486
|
-
|
|
487
|
-
|
|
483
|
+
let bodyWeights;
|
|
484
|
+
if (typography.body.weights) {
|
|
485
|
+
bodyWeights = [...typography.body.weights];
|
|
486
|
+
} else {
|
|
487
|
+
bodyWeights = [];
|
|
488
|
+
if (typography.body.weight) bodyWeights.push(typography.body.weight);
|
|
489
|
+
if (!bodyWeights.includes(400)) bodyWeights.push(400);
|
|
490
|
+
if (!bodyWeights.includes(700)) bodyWeights.push(700);
|
|
491
|
+
}
|
|
488
492
|
if (headingResolution && typography.body.family.toLowerCase() === headingResolution.family.toLowerCase()) {
|
|
489
493
|
const mergedWeights = Array.from(
|
|
490
494
|
/* @__PURE__ */ new Set([...headingResolution.weights, ...bodyWeights])
|
|
@@ -510,19 +514,22 @@ function resolveThemeFonts(typography, options) {
|
|
|
510
514
|
}
|
|
511
515
|
let displayResolution = null;
|
|
512
516
|
if (typography.display?.family) {
|
|
513
|
-
const displayWeights = [];
|
|
514
|
-
if (typography.display.weight) displayWeights.push(typography.display.weight);
|
|
517
|
+
const displayWeights = typography.display.weights ? [...typography.display.weights] : typography.display.weight ? [typography.display.weight] : [];
|
|
515
518
|
if (headingResolution && typography.display.family.toLowerCase() === headingResolution.family.toLowerCase()) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
519
|
+
if (typography.heading?.weights) {
|
|
520
|
+
displayResolution = headingResolution;
|
|
521
|
+
} else {
|
|
522
|
+
const mergedWeights = Array.from(
|
|
523
|
+
/* @__PURE__ */ new Set([...headingResolution.weights, ...displayWeights])
|
|
524
|
+
).sort((a, b) => a - b);
|
|
525
|
+
headingResolution = resolveFont(typography.heading.family, {
|
|
526
|
+
weights: mergedWeights,
|
|
527
|
+
display,
|
|
528
|
+
source: typography.heading.source,
|
|
529
|
+
org: typography.heading.org
|
|
530
|
+
});
|
|
531
|
+
displayResolution = headingResolution;
|
|
532
|
+
}
|
|
526
533
|
} else if (bodyResolution && typography.display.family.toLowerCase() === bodyResolution.family.toLowerCase()) {
|
|
527
534
|
const mergedWeights = Array.from(
|
|
528
535
|
/* @__PURE__ */ new Set([...bodyResolution.weights, ...displayWeights])
|
|
@@ -957,9 +964,9 @@ var LIGHTNESS_TARGETS = {
|
|
|
957
964
|
200: 0.87,
|
|
958
965
|
300: 0.78,
|
|
959
966
|
400: 0.65,
|
|
960
|
-
500:
|
|
961
|
-
|
|
962
|
-
|
|
967
|
+
500: -1,
|
|
968
|
+
// placeholder — replaced by input L at anchor (brand color lives at 500)
|
|
969
|
+
600: 0.45,
|
|
963
970
|
700: 0.38,
|
|
964
971
|
800: 0.3,
|
|
965
972
|
900: 0.22,
|
|
@@ -979,8 +986,8 @@ var CHROMA_MULTIPLIERS = {
|
|
|
979
986
|
950: 0.5
|
|
980
987
|
};
|
|
981
988
|
var ANCHOR_SHADE = {
|
|
982
|
-
primary:
|
|
983
|
-
accent:
|
|
989
|
+
primary: 500,
|
|
990
|
+
accent: 500,
|
|
984
991
|
neutral: 500,
|
|
985
992
|
success: 500,
|
|
986
993
|
warning: 500,
|
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-DgAumoCX.js';
|
|
2
|
+
export { k as ColorFormat, l as FontSource, m as RGBA, n as SemanticTokenValue } from './types-DgAumoCX.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Font resolver — maps font family names to loadable font resources.
|
package/dist/index.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
rgbToHex,
|
|
32
32
|
rgbToOklch,
|
|
33
33
|
serializeColor
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-NZF2MS4L.js";
|
|
35
35
|
|
|
36
36
|
// src/pipeline.ts
|
|
37
37
|
import { parse as parseYaml } from "yaml";
|
|
@@ -325,6 +325,8 @@ var KNOWN_TOP_LEVEL_KEYS = /* @__PURE__ */ new Set([
|
|
|
325
325
|
"name",
|
|
326
326
|
"version",
|
|
327
327
|
"group",
|
|
328
|
+
"label",
|
|
329
|
+
"default-mode",
|
|
328
330
|
"colors",
|
|
329
331
|
"colors-dark",
|
|
330
332
|
"typography",
|
|
@@ -353,7 +355,7 @@ var KNOWN_TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
|
|
|
353
355
|
"letter-spacing",
|
|
354
356
|
"scale"
|
|
355
357
|
]);
|
|
356
|
-
var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "source", "org"]);
|
|
358
|
+
var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
|
|
357
359
|
var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family"]);
|
|
358
360
|
var KNOWN_LETTER_SPACING_KEYS = /* @__PURE__ */ new Set(["tight", "normal", "wide"]);
|
|
359
361
|
var KNOWN_SPACING_KEYS = /* @__PURE__ */ new Set(["base"]);
|
|
@@ -473,6 +475,15 @@ function validateConfig(config) {
|
|
|
473
475
|
if (obj.version !== 1) {
|
|
474
476
|
errors.push("'version' must be 1");
|
|
475
477
|
}
|
|
478
|
+
if (obj.label !== void 0 && typeof obj.label !== "string") {
|
|
479
|
+
errors.push("'label' must be a string (optional display name override)");
|
|
480
|
+
}
|
|
481
|
+
if (obj["default-mode"] !== void 0) {
|
|
482
|
+
const mode = obj["default-mode"];
|
|
483
|
+
if (mode !== "dark" && mode !== "light") {
|
|
484
|
+
errors.push("'default-mode' must be either 'dark' or 'light'");
|
|
485
|
+
}
|
|
486
|
+
}
|
|
476
487
|
if (typeof obj.colors !== "object" || obj.colors === null) {
|
|
477
488
|
errors.push("'colors' is required and must be an object");
|
|
478
489
|
return { valid: false, errors };
|
|
@@ -530,6 +541,11 @@ function validateConfig(config) {
|
|
|
530
541
|
if (font && font.source === "visor-fonts" && !font.org) {
|
|
531
542
|
errors.push(`'typography.${slot}.org' is required when source is 'visor-fonts'`);
|
|
532
543
|
}
|
|
544
|
+
if (font && font.weights !== void 0) {
|
|
545
|
+
if (!Array.isArray(font.weights) || !font.weights.every((w) => typeof w === "number" && w > 0)) {
|
|
546
|
+
errors.push(`'typography.${slot}.weights' must be an array of positive numbers (e.g., [300, 500])`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
533
549
|
}
|
|
534
550
|
}
|
|
535
551
|
if (obj.overrides !== void 0) {
|
|
@@ -636,19 +652,22 @@ function resolveConfig(config) {
|
|
|
636
652
|
family: config.typography?.heading?.family ?? DEFAULTS.typography.heading.family,
|
|
637
653
|
weight: config.typography?.heading?.weight ?? DEFAULTS.typography.heading.weight,
|
|
638
654
|
...config.typography?.heading?.source && { source: config.typography.heading.source },
|
|
639
|
-
...config.typography?.heading?.org && { org: config.typography.heading.org }
|
|
655
|
+
...config.typography?.heading?.org && { org: config.typography.heading.org },
|
|
656
|
+
...config.typography?.heading?.weights && { weights: config.typography.heading.weights }
|
|
640
657
|
},
|
|
641
658
|
display: {
|
|
642
659
|
family: config.typography?.display?.family ?? config.typography?.heading?.family ?? DEFAULTS.typography.heading.family,
|
|
643
660
|
weight: config.typography?.display?.weight ?? 400,
|
|
644
661
|
...config.typography?.display?.source && { source: config.typography.display.source },
|
|
645
|
-
...config.typography?.display?.org && { org: config.typography.display.org }
|
|
662
|
+
...config.typography?.display?.org && { org: config.typography.display.org },
|
|
663
|
+
...config.typography?.display?.weights && { weights: config.typography.display.weights }
|
|
646
664
|
},
|
|
647
665
|
body: {
|
|
648
666
|
family: config.typography?.body?.family ?? DEFAULTS.typography.body.family,
|
|
649
667
|
weight: config.typography?.body?.weight ?? DEFAULTS.typography.body.weight,
|
|
650
668
|
...config.typography?.body?.source && { source: config.typography.body.source },
|
|
651
|
-
...config.typography?.body?.org && { org: config.typography.body.org }
|
|
669
|
+
...config.typography?.body?.org && { org: config.typography.body.org },
|
|
670
|
+
...config.typography?.body?.weights && { weights: config.typography.body.weights }
|
|
652
671
|
},
|
|
653
672
|
mono: {
|
|
654
673
|
family: config.typography?.mono?.family ?? DEFAULTS.typography.mono.family
|
|
@@ -1835,6 +1854,51 @@ function checkRadiusScale(config, issues) {
|
|
|
1835
1854
|
);
|
|
1836
1855
|
}
|
|
1837
1856
|
}
|
|
1857
|
+
function checkDarkLightParity(config, issues) {
|
|
1858
|
+
if (!config.colors) return;
|
|
1859
|
+
const colorKeys = Object.keys(config.colors).filter((k) => k !== "primary");
|
|
1860
|
+
const hasDarkSection = config["colors-dark"] !== void 0;
|
|
1861
|
+
if (colorKeys.length > 0 && !hasDarkSection) {
|
|
1862
|
+
issues.push(
|
|
1863
|
+
issue(
|
|
1864
|
+
"warning",
|
|
1865
|
+
"DARK_LIGHT_PARITY",
|
|
1866
|
+
"Custom colors are set but no colors-dark section exists. Dark mode will use generated defaults which may not match your brand.",
|
|
1867
|
+
"colors-dark"
|
|
1868
|
+
)
|
|
1869
|
+
);
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (colorKeys.length > 0 && hasDarkSection) {
|
|
1873
|
+
const lightKeys = new Set(Object.keys(config.colors));
|
|
1874
|
+
const darkKeys = new Set(Object.keys(config["colors-dark"]));
|
|
1875
|
+
for (const key of lightKeys) {
|
|
1876
|
+
if (key === "primary") continue;
|
|
1877
|
+
if (!darkKeys.has(key)) {
|
|
1878
|
+
issues.push(
|
|
1879
|
+
issue(
|
|
1880
|
+
"warning",
|
|
1881
|
+
"DARK_LIGHT_PARITY",
|
|
1882
|
+
`Color "${key}" is set in colors but missing from colors-dark. Dark mode will use a generated default.`,
|
|
1883
|
+
"colors-dark"
|
|
1884
|
+
)
|
|
1885
|
+
);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
for (const key of darkKeys) {
|
|
1889
|
+
if (!lightKeys.has(key)) {
|
|
1890
|
+
issues.push(
|
|
1891
|
+
issue(
|
|
1892
|
+
"warning",
|
|
1893
|
+
"DARK_LIGHT_PARITY",
|
|
1894
|
+
`Color "${key}" is set in colors-dark but missing from colors. Light mode will use a generated default.`,
|
|
1895
|
+
"colors"
|
|
1896
|
+
)
|
|
1897
|
+
);
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1838
1902
|
function validate(config) {
|
|
1839
1903
|
const errors = [];
|
|
1840
1904
|
const warnings = [];
|
|
@@ -1866,6 +1930,7 @@ function validate(config) {
|
|
|
1866
1930
|
checkColorSimilarity(typedConfig, warnings);
|
|
1867
1931
|
checkMissingGlowShadow(typedConfig, warnings);
|
|
1868
1932
|
checkRadiusScale(typedConfig, warnings);
|
|
1933
|
+
checkDarkLightParity(typedConfig, warnings);
|
|
1869
1934
|
}
|
|
1870
1935
|
return {
|
|
1871
1936
|
valid: errors.length === 0,
|
|
@@ -46,18 +46,24 @@ interface VisorTypography {
|
|
|
46
46
|
heading?: {
|
|
47
47
|
family: string;
|
|
48
48
|
weight?: number;
|
|
49
|
+
/** Explicit list of font weights to load (overrides engine defaults) */
|
|
50
|
+
weights?: number[];
|
|
49
51
|
source?: FontSource;
|
|
50
52
|
org?: string;
|
|
51
53
|
};
|
|
52
54
|
display?: {
|
|
53
55
|
family: string;
|
|
54
56
|
weight?: number;
|
|
57
|
+
/** Explicit list of font weights to load (overrides engine defaults) */
|
|
58
|
+
weights?: number[];
|
|
55
59
|
source?: FontSource;
|
|
56
60
|
org?: string;
|
|
57
61
|
};
|
|
58
62
|
body?: {
|
|
59
63
|
family: string;
|
|
60
64
|
weight?: number;
|
|
65
|
+
/** Explicit list of font weights to load (overrides engine defaults) */
|
|
66
|
+
weights?: number[];
|
|
61
67
|
source?: FontSource;
|
|
62
68
|
org?: string;
|
|
63
69
|
};
|
|
@@ -123,6 +129,10 @@ interface VisorThemeConfig {
|
|
|
123
129
|
version: 1;
|
|
124
130
|
/** Theme group for the docs site theme switcher (e.g. 'Visor', 'Client', 'Low Orbit'). Used by `visor theme sync`. */
|
|
125
131
|
group?: string;
|
|
132
|
+
/** Optional display label override for the theme switcher (e.g. 'ENTR', 'SoleSpark'). Falls back to title-cased name. */
|
|
133
|
+
label?: string;
|
|
134
|
+
/** Default color mode to force when the theme is activated ('dark' or 'light'). If unset, user/system preference applies. */
|
|
135
|
+
"default-mode"?: "dark" | "light";
|
|
126
136
|
colors: {
|
|
127
137
|
primary: string;
|
|
128
138
|
accent?: string;
|
|
@@ -150,18 +160,21 @@ interface VisorThemeConfig {
|
|
|
150
160
|
heading?: {
|
|
151
161
|
family?: string;
|
|
152
162
|
weight?: number;
|
|
163
|
+
weights?: number[];
|
|
153
164
|
source?: FontSource;
|
|
154
165
|
org?: string;
|
|
155
166
|
};
|
|
156
167
|
display?: {
|
|
157
168
|
family?: string;
|
|
158
169
|
weight?: number;
|
|
170
|
+
weights?: number[];
|
|
159
171
|
source?: FontSource;
|
|
160
172
|
org?: string;
|
|
161
173
|
};
|
|
162
174
|
body?: {
|
|
163
175
|
family?: string;
|
|
164
176
|
weight?: number;
|
|
177
|
+
weights?: number[];
|
|
165
178
|
source?: FontSource;
|
|
166
179
|
org?: string;
|
|
167
180
|
};
|
|
@@ -226,18 +239,21 @@ interface ResolvedThemeConfig {
|
|
|
226
239
|
heading: {
|
|
227
240
|
family: string;
|
|
228
241
|
weight: number;
|
|
242
|
+
weights?: number[];
|
|
229
243
|
source?: FontSource;
|
|
230
244
|
org?: string;
|
|
231
245
|
};
|
|
232
246
|
display: {
|
|
233
247
|
family: string;
|
|
234
248
|
weight: number;
|
|
249
|
+
weights?: number[];
|
|
235
250
|
source?: FontSource;
|
|
236
251
|
org?: string;
|
|
237
252
|
};
|
|
238
253
|
body: {
|
|
239
254
|
family: string;
|
|
240
255
|
weight: number;
|
|
256
|
+
weights?: number[];
|
|
241
257
|
source?: FontSource;
|
|
242
258
|
org?: string;
|
|
243
259
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loworbitstudio/visor-theme-engine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.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",
|
|
@@ -35,7 +35,12 @@
|
|
|
35
35
|
"design-system",
|
|
36
36
|
"theme",
|
|
37
37
|
"oklch",
|
|
38
|
-
"tokens"
|
|
38
|
+
"tokens",
|
|
39
|
+
"react",
|
|
40
|
+
"css-variables",
|
|
41
|
+
"theming",
|
|
42
|
+
"color-system",
|
|
43
|
+
"wcag"
|
|
39
44
|
],
|
|
40
45
|
"license": "MIT",
|
|
41
46
|
"repository": {
|