@loworbitstudio/visor-theme-engine 0.28.0 → 0.29.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.
@@ -1,4 +1,4 @@
1
- import { k as GeneratedPrimitives, s as SemanticTokens, R as ResolvedThemeConfig } from '../types-ZPTjTL_-.js';
1
+ import { k as GeneratedPrimitives, s as SemanticTokens, R as ResolvedThemeConfig } from '../types-C7nWZ5fB.js';
2
2
 
3
3
  /**
4
4
  * Adapter types for the Visor theme engine.
@@ -2,6 +2,8 @@ import {
2
2
  FULL_SHADE_STEPS,
3
3
  MATERIAL_TEXT_SLOTS,
4
4
  SELECTIVE_SHADE_STEPS,
5
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
6
+ VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST,
5
7
  aliasFamily,
6
8
  block,
7
9
  buildVisorFontUrl,
@@ -11,6 +13,7 @@ import {
11
13
  generateDarkCss,
12
14
  generateFontMetricDecls,
13
15
  generateFontSizeDecls,
16
+ generateFontTextTransformDecls,
14
17
  generateFontWeightDecls,
15
18
  generateHairlineDecls,
16
19
  generateIntentDecls,
@@ -26,7 +29,7 @@ import {
26
29
  resolveThemeBrand,
27
30
  resolveThemeFonts,
28
31
  sectionComment
29
- } from "../chunk-X2SWJPM2.js";
32
+ } from "../chunk-YMCDUC7N.js";
30
33
 
31
34
  // src/adapters/brand-passthrough.ts
32
35
  var SENTINEL_COLOR = "#ff00ff";
@@ -308,10 +311,11 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
308
311
  const darkBody = stripHeader(generateDarkCss(input.tokens, { scopePrefix, colorScheme }));
309
312
  const adaptiveBody = [lightBody, darkBody].filter(Boolean).join("\n\n");
310
313
  if (scopePrefix) {
311
- const declared = collectDeclaredProperties(
312
- [primitivesBody, semanticLines.join("\n"), adaptiveBody].join("\n")
313
- );
314
- const aliasDecls = generateSemanticAliasDecls(declared);
314
+ const themeCss = [primitivesBody, semanticLines.join("\n"), adaptiveBody].join("\n");
315
+ const declared = collectDeclaredProperties(themeCss);
316
+ const aliasDecls = generateSemanticAliasDecls(declared, {
317
+ table: colorScheme === "dark-only" ? VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST : void 0
318
+ });
315
319
  if (aliasDecls.length > 0) {
316
320
  semanticLines.push(
317
321
  sectionComment("visor-core alias re-substitution at theme scope (VI-648)")
@@ -319,6 +323,27 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
319
323
  semanticLines.push(block(hostSelector, aliasDecls));
320
324
  semanticLines.push("");
321
325
  }
326
+ if (colorScheme === "adaptive") {
327
+ const darkAliasDecls = generateSemanticAliasDecls(
328
+ collectDeclaredProperties(themeCss, { exclude: [lightModeSelector] }),
329
+ { table: VISOR_CORE_DARK_SEMANTIC_ALIASES }
330
+ );
331
+ if (darkAliasDecls.length > 0) {
332
+ semanticLines.push(
333
+ sectionComment("visor-core alias re-substitution (dark) \u2014 manual toggle (VI-696)")
334
+ );
335
+ semanticLines.push(block(darkSelectors.join(",\n"), darkAliasDecls));
336
+ semanticLines.push("");
337
+ semanticLines.push(
338
+ sectionComment("visor-core alias re-substitution (dark) \u2014 prefers-color-scheme (VI-696)")
339
+ );
340
+ const inner = block(prefersSelector, darkAliasDecls);
341
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
342
+ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
343
+ }`);
344
+ semanticLines.push("");
345
+ }
346
+ }
322
347
  }
323
348
  const semanticLayer = wrapInLayer("visor-semantic", semanticLines.join("\n").trim());
324
349
  if (semanticLayer) {
@@ -562,6 +587,7 @@ function generateTypographyDecls(config, aliases) {
562
587
  decls.push(...generateFontSizeDecls(config.typography.scale));
563
588
  decls.push(...generateFontMetricDecls(config.typography.body.family));
564
589
  decls.push(...generateFontWeightDecls(config.typography));
590
+ decls.push(...generateFontTextTransformDecls(config.typography));
565
591
  const lineHeights = {
566
592
  none: 1,
567
593
  tight: 1.25,
@@ -900,6 +926,33 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
900
926
  },
901
927
  colorScheme
902
928
  );
929
+ const themeCss = [lines.join("\n"), semanticLines.join("\n"), componentTokensCss, brandResult.css, passthroughCss].join("\n");
930
+ const declared = collectDeclaredProperties(themeCss);
931
+ const aliasDecls = generateSemanticAliasDecls(declared, {
932
+ table: colorScheme === "dark-only" ? VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST : void 0
933
+ });
934
+ if (aliasDecls.length > 0) {
935
+ semanticLines.push(sectionComment2("visor-core alias re-substitution at theme scope (VI-695)"));
936
+ semanticLines.push(block2(scopeClass, aliasDecls));
937
+ semanticLines.push("");
938
+ }
939
+ if (emitPrefers) {
940
+ const darkAliasDecls = generateSemanticAliasDecls(
941
+ collectDeclaredProperties(themeCss, { exclude: [lightSel] }),
942
+ { table: VISOR_CORE_DARK_SEMANTIC_ALIASES }
943
+ );
944
+ if (darkAliasDecls.length > 0) {
945
+ semanticLines.push(sectionComment2("visor-core alias re-substitution (dark) \u2014 manual toggle (VI-696)"));
946
+ semanticLines.push(block2(darkSel, darkAliasDecls));
947
+ semanticLines.push("");
948
+ semanticLines.push(sectionComment2("visor-core alias re-substitution (dark) \u2014 prefers-color-scheme (VI-696)"));
949
+ const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, darkAliasDecls);
950
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
951
+ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
952
+ }`);
953
+ semanticLines.push("");
954
+ }
955
+ }
903
956
  const adaptiveBody = [lines.join("\n").trim(), componentTokensCss].filter(Boolean).join("\n\n");
904
957
  const adaptiveLayer = wrapInLayer("visor-adaptive", adaptiveBody);
905
958
  const semanticLayer = wrapInLayer("visor-semantic", semanticLines.join("\n").trim());
@@ -1988,7 +1988,7 @@ var textFamily = {
1988
1988
  var switchFamily = {
1989
1989
  family: "switch",
1990
1990
  prefix: "switch",
1991
- description: 'Switch track and knob dimensions (default size; `size="sm"` keeps its own). Set `knob-inset` to size the knob from the track: it sits that far from the track\'s outer edge on every side.',
1991
+ description: 'Switch track and knob dimensions (default size; `size="sm"` keeps its own) and colours. Set `knob-inset` to size the knob from the track: it sits that far from the track\'s outer edge on every side.',
1992
1992
  tokens: [
1993
1993
  { key: "track-width", property: "width", description: "Track width.", consumers: at(SWITCH, "2rem") },
1994
1994
  {
@@ -2009,6 +2009,30 @@ var switchFamily = {
2009
2009
  property: "outline-color",
2010
2010
  description: "Track edge colour \u2014 transparent by default. Width and style come from the shared `control` edge, so `control.edge-width: 0` turns it off.",
2011
2011
  consumers: at(SWITCH, "transparent")
2012
+ },
2013
+ {
2014
+ key: "track-hover-bg",
2015
+ property: "background-color (hover)",
2016
+ description: "Unchecked track fill under the pointer. Bind it in a borderless theme, where `--border-strong` is transparent.",
2017
+ consumers: at(SWITCH, "var(--border-strong, #d1d5db)")
2018
+ },
2019
+ {
2020
+ key: "track-hover-bg-checked",
2021
+ property: "background-color (hover)",
2022
+ description: "Checked track fill under the pointer. Unbound it is `--border-strong`, as it always was; bind it to keep the checked fill under the pointer.",
2023
+ consumers: at(SWITCH, "var(--border-strong, #d1d5db)")
2024
+ },
2025
+ {
2026
+ key: "knob-bg",
2027
+ property: "background-color (knob)",
2028
+ description: "Unchecked knob fill. Bind it in a dark theme, where the page colour is a black knob on a dark track.",
2029
+ consumers: at(SWITCH, "var(--surface-page, #ffffff)")
2030
+ },
2031
+ {
2032
+ key: "knob-bg-checked",
2033
+ property: "background-color (knob)",
2034
+ description: "Checked knob fill. Independent of `knob-bg`, so a light unchecked knob can sit beside a dark knob on a bright checked track.",
2035
+ consumers: at(SWITCH, "var(--surface-page, #ffffff)")
2012
2036
  }
2013
2037
  ]
2014
2038
  };
@@ -2720,6 +2744,14 @@ function generateFontSizeDecls(scale) {
2720
2744
  return `--font-size-${name}: ${rem}rem; /* ${Number(scaled.toFixed(2))}px */`;
2721
2745
  });
2722
2746
  }
2747
+ function generateFontTextTransformDecls(typography) {
2748
+ const decls = [];
2749
+ for (const slot of ["heading", "display", "body", "mono"]) {
2750
+ const value = typography[slot]["text-transform"];
2751
+ if (value) decls.push(`--font-${slot}-text-transform: ${value};`);
2752
+ }
2753
+ return decls;
2754
+ }
2723
2755
  function generateTypographyPrimitives(config, aliases = EMPTY_ALIASES) {
2724
2756
  const decls = [];
2725
2757
  decls.push(`--font-heading: ${fontStack(config.typography.heading.family, aliases)};`);
@@ -2730,6 +2762,7 @@ function generateTypographyPrimitives(config, aliases = EMPTY_ALIASES) {
2730
2762
  decls.push(...generateFontSizeDecls(config.typography.scale));
2731
2763
  decls.push(...generateFontMetricDecls(config.typography.body.family));
2732
2764
  decls.push(...generateFontWeightDecls(config.typography));
2765
+ decls.push(...generateFontTextTransformDecls(config.typography));
2733
2766
  const lineHeights = {
2734
2767
  none: 1,
2735
2768
  tight: 1.25,
@@ -3134,16 +3167,46 @@ var VISOR_CORE_SEMANTIC_ALIASES = {
3134
3167
  // Field-attached floating panels (VI-497).
3135
3168
  "field-menu-bg": "surface-popover"
3136
3169
  };
3137
- function collectDeclaredProperties(css) {
3170
+ var VISOR_CORE_DARK_SEMANTIC_ALIASES = {
3171
+ "border-input": "color-neutral-700",
3172
+ "skeleton-from": "color-neutral-800",
3173
+ "skeleton-to": "color-neutral-700",
3174
+ "chart-1": "color-primary-400",
3175
+ "sidebar-bg": "color-neutral-900",
3176
+ "sidebar-text": "color-neutral-300",
3177
+ "sidebar-primary-bg": "color-primary-500",
3178
+ "sidebar-accent-bg": "color-neutral-800",
3179
+ "sidebar-accent-text": "color-neutral-50",
3180
+ "sidebar-border": "color-neutral-700",
3181
+ "sidebar-ring": "color-primary-400",
3182
+ "sidebar-text-muted": "color-neutral-400"
3183
+ };
3184
+ var MODE_DEPENDENT_SEMANTIC_ALIASES = new Set(
3185
+ Object.keys(VISOR_CORE_DARK_SEMANTIC_ALIASES)
3186
+ );
3187
+ var VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST = {
3188
+ ...VISOR_CORE_SEMANTIC_ALIASES,
3189
+ ...VISOR_CORE_DARK_SEMANTIC_ALIASES
3190
+ };
3191
+ function collectDeclaredProperties(css, options = {}) {
3138
3192
  const declared = /* @__PURE__ */ new Set();
3139
- for (const match of css.matchAll(/(^|[{;\s])--([a-zA-Z0-9-]+)\s*:/g)) {
3140
- declared.add(match[2]);
3193
+ if (!options.exclude?.length) {
3194
+ for (const match of css.matchAll(/(^|[{;\s])--([a-zA-Z0-9-]+)\s*:/g)) {
3195
+ declared.add(match[2]);
3196
+ }
3197
+ return declared;
3198
+ }
3199
+ const exclude = new Set(options.exclude);
3200
+ for (const rule of css.replace(/\/\*[\s\S]*?\*\//g, "").matchAll(/([^{}]*)\{([^{}]*)\}/g)) {
3201
+ if (exclude.has(rule[1].trim())) continue;
3202
+ for (const prop of collectDeclaredProperties(rule[2])) declared.add(prop);
3141
3203
  }
3142
3204
  return declared;
3143
3205
  }
3144
- function generateSemanticAliasDecls(declared) {
3206
+ function generateSemanticAliasDecls(declared, options = {}) {
3145
3207
  const decls = [];
3146
- for (const [alias, referent] of Object.entries(VISOR_CORE_SEMANTIC_ALIASES)) {
3208
+ for (const [alias, referent] of Object.entries(options.table ?? VISOR_CORE_SEMANTIC_ALIASES)) {
3209
+ if (options.skip?.has(alias)) continue;
3147
3210
  if (declared.has(alias)) continue;
3148
3211
  if (!declared.has(referent)) continue;
3149
3212
  decls.push(`--${alias}: var(--${referent});`);
@@ -3152,6 +3215,7 @@ function generateSemanticAliasDecls(declared) {
3152
3215
  }
3153
3216
 
3154
3217
  // src/types.ts
3218
+ var TEXT_TRANSFORMS = ["none", "uppercase", "lowercase", "capitalize"];
3155
3219
  var MATERIAL_TEXT_SLOTS = [
3156
3220
  "displayLarge",
3157
3221
  "displayMedium",
@@ -3208,6 +3272,7 @@ export {
3208
3272
  isValidColor,
3209
3273
  compositeOverBackground,
3210
3274
  serializeColor,
3275
+ TEXT_TRANSFORMS,
3211
3276
  MATERIAL_TEXT_SLOTS,
3212
3277
  componentTokenName,
3213
3278
  COMPONENT_TOKEN_FAMILIES,
@@ -3238,6 +3303,7 @@ export {
3238
3303
  sectionComment,
3239
3304
  block2 as block,
3240
3305
  generateFontSizeDecls,
3306
+ generateFontTextTransformDecls,
3241
3307
  generatePrimitivesCss,
3242
3308
  generateSemanticCss,
3243
3309
  generateTextScaleAliasDecls,
@@ -3248,6 +3314,9 @@ export {
3248
3314
  generateDarkCss,
3249
3315
  generateFullBundleCss,
3250
3316
  VISOR_CORE_SEMANTIC_ALIASES,
3317
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
3318
+ MODE_DEPENDENT_SEMANTIC_ALIASES,
3319
+ VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST,
3251
3320
  collectDeclaredProperties,
3252
3321
  generateSemanticAliasDecls
3253
3322
  };
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, c as VisorBrand, B as BrandSlot, d as BrandSource, e as BrandResolution, C as ColorScheme, f as ThemeBrandResult, g as BrandStrategy, h as BrandStrategyContext, i as BrandStrategyIssue, j as BrandStrategyValidationResult, S as SerializedBrandStrategy, R as ResolvedThemeConfig, k as GeneratedPrimitives, l as ThemeOutput, m as ThemeData, n as VisorThemeConfig, o as FullShadeScale, p as ColorRole, q as SelectiveShadeScale, r as RGB, P as ParsedColor, O as OKLCH, s as SemanticTokens, t as ShadeStep } from './types-ZPTjTL_-.js';
2
- export { u as BRAND_VARIANTS, v as BRAND_VISIBILITIES, w as BrandAccessibility, x as BrandArchetype, y as BrandBoilerplate, z as BrandColorPairing, A as BrandColorUsage, D as BrandContrastTarget, E as BrandGoverns, H as BrandLexiconEntry, I as BrandMessaging, J as BrandPersonalityTrait, K as BrandPillar, L as BrandPositioning, M as BrandStrategyIssueSeverity, N as BrandToneEntry, Q as BrandVariant, U as BrandVisibility, W as BrandVoice, X as BrandVoiceTrait, Y as COMPONENT_TOKEN_FAMILIES, Z as COMPONENT_TOKEN_FAMILY_BY_NAME, _ as ColorFormat, $ as ComponentTokenBinding, a0 as ComponentTokenBindings, a1 as ComponentTokenConsumer, a2 as ComponentTokenFamily, a3 as ComponentTokenSpec, a4 as DEFAULT_BRAND_STRATEGY_SURFACES, a5 as DEFAULT_BRAND_STRATEGY_TONE_STATES, a6 as FontSource, a7 as GOVERNS_WILDCARD, a8 as RGBA, a9 as ResolvedComponentTokens, aa as SemanticTokenValue, ab as allComponentTokenNames, ac as componentTokenName, ad as hasComponentBindings, ae as resolveComponentBindings, af as validateComponentBindings } from './types-ZPTjTL_-.js';
1
+ import { F as FontResolveOptions, a as FontResolution, V as VisorTypography, b as FontDisplayStrategy, T as ThemeFontResult, G as GoogleFontEntry, c as VisorBrand, B as BrandSlot, d as BrandSource, e as BrandResolution, C as ColorScheme, f as ThemeBrandResult, g as BrandStrategy, h as BrandStrategyContext, i as BrandStrategyIssue, j as BrandStrategyValidationResult, S as SerializedBrandStrategy, R as ResolvedThemeConfig, k as GeneratedPrimitives, l as ThemeOutput, m as ThemeData, n as VisorThemeConfig, o as FullShadeScale, p as ColorRole, q as SelectiveShadeScale, r as RGB, P as ParsedColor, O as OKLCH, s as SemanticTokens, t as ShadeStep } from './types-C7nWZ5fB.js';
2
+ export { u as BRAND_VARIANTS, v as BRAND_VISIBILITIES, w as BrandAccessibility, x as BrandArchetype, y as BrandBoilerplate, z as BrandColorPairing, A as BrandColorUsage, D as BrandContrastTarget, E as BrandGoverns, H as BrandLexiconEntry, I as BrandMessaging, J as BrandPersonalityTrait, K as BrandPillar, L as BrandPositioning, M as BrandStrategyIssueSeverity, N as BrandToneEntry, Q as BrandVariant, U as BrandVisibility, W as BrandVoice, X as BrandVoiceTrait, Y as COMPONENT_TOKEN_FAMILIES, Z as COMPONENT_TOKEN_FAMILY_BY_NAME, _ as ColorFormat, $ as ComponentTokenBinding, a0 as ComponentTokenBindings, a1 as ComponentTokenConsumer, a2 as ComponentTokenFamily, a3 as ComponentTokenSpec, a4 as DEFAULT_BRAND_STRATEGY_SURFACES, a5 as DEFAULT_BRAND_STRATEGY_TONE_STATES, a6 as FontSource, a7 as GOVERNS_WILDCARD, a8 as RGBA, a9 as ResolvedComponentTokens, aa as SemanticTokenValue, ab as allComponentTokenNames, ac as componentTokenName, ad as hasComponentBindings, ae as resolveComponentBindings, af as validateComponentBindings } from './types-C7nWZ5fB.js';
3
3
 
4
4
  /**
5
5
  * Font resolver — maps font family names to loadable font resources.
@@ -527,6 +527,16 @@ var properties = {
527
527
  org: {
528
528
  type: "string",
529
529
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
530
+ },
531
+ "text-transform": {
532
+ type: "string",
533
+ "enum": [
534
+ "none",
535
+ "uppercase",
536
+ "lowercase",
537
+ "capitalize"
538
+ ],
539
+ description: "Casing applied to this slot's text via --font-heading-text-transform. Emitted only when set."
530
540
  }
531
541
  }
532
542
  },
@@ -557,6 +567,16 @@ var properties = {
557
567
  org: {
558
568
  type: "string",
559
569
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
570
+ },
571
+ "text-transform": {
572
+ type: "string",
573
+ "enum": [
574
+ "none",
575
+ "uppercase",
576
+ "lowercase",
577
+ "capitalize"
578
+ ],
579
+ description: "Casing applied to this slot's text via --font-display-text-transform. Emitted only when set."
560
580
  }
561
581
  }
562
582
  },
@@ -586,6 +606,16 @@ var properties = {
586
606
  org: {
587
607
  type: "string",
588
608
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
609
+ },
610
+ "text-transform": {
611
+ type: "string",
612
+ "enum": [
613
+ "none",
614
+ "uppercase",
615
+ "lowercase",
616
+ "capitalize"
617
+ ],
618
+ description: "Casing applied to this slot's text via --font-body-text-transform. Emitted only when set."
589
619
  }
590
620
  }
591
621
  },
@@ -596,6 +626,16 @@ var properties = {
596
626
  family: {
597
627
  type: "string",
598
628
  description: "Google Fonts family name or CSS font stack for monospace."
629
+ },
630
+ "text-transform": {
631
+ type: "string",
632
+ "enum": [
633
+ "none",
634
+ "uppercase",
635
+ "lowercase",
636
+ "capitalize"
637
+ ],
638
+ description: "Casing applied to this slot's text via --font-mono-text-transform. Emitted only when set."
599
639
  }
600
640
  }
601
641
  },
@@ -1216,7 +1256,7 @@ var properties = {
1216
1256
  },
1217
1257
  "switch": {
1218
1258
  type: "object",
1219
- description: "Switch track and knob dimensions (default size; `size=\"sm\"` keeps its own). Set `knob-inset` to size the knob from the track: it sits that far from the track's outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color.",
1259
+ description: "Switch track and knob dimensions (default size; `size=\"sm\"` keeps its own) and colours. Set `knob-inset` to size the knob from the track: it sits that far from the track's outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color, track-hover-bg, track-hover-bg-checked, knob-bg, knob-bg-checked.",
1220
1260
  additionalProperties: {
1221
1261
  $ref: "#/$defs/componentTokenBinding"
1222
1262
  }
@@ -2054,6 +2094,25 @@ declare function getKnownTokenRefs(): Set<string>;
2054
2094
  * a diff against that file readable.
2055
2095
  */
2056
2096
  declare const VISOR_CORE_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2097
+ /**
2098
+ * Aliases visor-core declares with a **different referent in dark mode**, mapped
2099
+ * to that dark referent. `--sidebar-bg` is `var(--color-neutral-50)` on `:root`
2100
+ * but `var(--color-neutral-900)` under `[data-theme="dark"]` and the
2101
+ * prefers-color-scheme block.
2102
+ *
2103
+ * `VISOR_CORE_SEMANTIC_ALIASES` records the light referent. A theme scope
2104
+ * re-declares these with the light referent on the host and with the dark one
2105
+ * on its dark selectors (VI-696), so a scoped dark sidebar resolves to the
2106
+ * theme's own `neutral-900`, not its near-white `neutral-50` (the VI-648 flat
2107
+ * emission) nor visor-core's untuned default (the VI-695 skip).
2108
+ *
2109
+ * Pinned against the dark rules of the emitted `tokens.css` by
2110
+ * `semantic-alias-coverage`: both the key set and every referent. A new
2111
+ * mode-dependent alias, or a changed dark referent, fails there by name.
2112
+ */
2113
+ declare const VISOR_CORE_DARK_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2114
+ /** The aliases in `VISOR_CORE_DARK_SEMANTIC_ALIASES`: the ones whose referent changes with the mode. */
2115
+ declare const MODE_DEPENDENT_SEMANTIC_ALIASES: ReadonlySet<string>;
2057
2116
  /**
2058
2117
  * Every custom property declared anywhere in `css`, without the leading `--`.
2059
2118
  *
@@ -2061,8 +2120,13 @@ declare const VISOR_CORE_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2061
2120
  * `generateSemanticAliasDecls` reflects what this theme *actually* emits —
2062
2121
  * including conditional output such as the `--font-weight-<n>` ladder, which
2063
2122
  * only appears when a theme declares an explicit `weights` array.
2123
+ *
2124
+ * `options.exclude` drops the rules whose selector is listed, for a caller that
2125
+ * needs only what one mode's selector can see (VI-696).
2064
2126
  */
2065
- declare function collectDeclaredProperties(css: string): Set<string>;
2127
+ declare function collectDeclaredProperties(css: string, options?: {
2128
+ exclude?: readonly string[];
2129
+ }): Set<string>;
2066
2130
  /**
2067
2131
  * The `--alias: var(--referent);` declarations to re-emit on a theme's scope
2068
2132
  * selector, given everything the theme has emitted so far.
@@ -2079,11 +2143,20 @@ declare function collectDeclaredProperties(css: string): Set<string>;
2079
2143
  * the correct outcome — re-emitting would replace a working value with an
2080
2144
  * invalid one.
2081
2145
  *
2146
+ * `options.skip` names aliases to leave inheriting visor-core's value.
2147
+ *
2148
+ * `options.table` swaps the alias → referent table. A dark selector passes
2149
+ * `VISOR_CORE_DARK_SEMANTIC_ALIASES`, with `declared` read off only what that
2150
+ * selector sees; a dark-only host passes `VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST`.
2151
+ *
2082
2152
  * Returns `[]` for a `:root`-scoped theme's caller, which does not need this at
2083
2153
  * all: there, visor-core's aliases and the theme's primitives are declared on
2084
2154
  * the same element, so substitution already sees the theme's values.
2085
2155
  */
2086
- declare function generateSemanticAliasDecls(declared: ReadonlySet<string>): string[];
2156
+ declare function generateSemanticAliasDecls(declared: ReadonlySet<string>, options?: {
2157
+ skip?: ReadonlySet<string>;
2158
+ table?: Readonly<Record<string, string>>;
2159
+ }): string[];
2087
2160
 
2088
2161
  /**
2089
2162
  * Per-theme font-family aliasing — substrate fix for VI-354.
@@ -2367,4 +2440,4 @@ declare const FONT_VERTICAL_METRICS: Record<string, FontVerticalMetrics>;
2367
2440
  /** The `--font-ascent` / `--font-descent` declarations for a body family, or none when the face is unmeasured. */
2368
2441
  declare function generateFontMetricDecls(family: string): string[];
2369
2442
 
2370
- export { type BrandPassthrough, BrandResolution, BrandSlot, BrandSource, BrandStrategy, BrandStrategyContext, BrandStrategyIssue, BrandStrategyValidationResult, type CSSFile, ColorRole, type Confidence, DEFAULT_VISOR_BRAND, type ExtractedToken, type ExtractionResult, FONT_VERTICAL_METRICS, FONT_WEIGHT_ALIASES, type FontCoverageError, type FontCoverageResult, FontDisplayStrategy, type FontFaceDeclaration, FontResolution, FontResolveOptions, type FontVerticalMetrics, FullShadeScale, GeneratedPrimitives, GoogleFontEntry, OKLCH, ParsedColor, RGB, ResolvedThemeConfig, type ResolvedWeightRamp, SEMANTIC_MAP, SelectiveShadeScale, SemanticTokens, SerializedBrandStrategy, ShadeStep, TAILWIND_GRAY, ThemeBrandResult, ThemeData, ThemeFontResult, ThemeOutput, type ThemeValidationResult, VISOR_BRANDS_CDN, VISOR_CORE_SEMANTIC_ALIASES, VISOR_DEFAULT_BRAND_PATH, VISOR_FONTS_CDN, type ValidateOptions, type ValidationIssue, type ValidationSeverity, VisorBrand, VisorThemeConfig, VisorTypography, WEIGHT_RAMP_TARGETS, applyOverrides, assignSemanticTokens, buildVisorBrandUrl, buildVisorFontUrl, checkBrandStrategyCoherence, checkBrandStrategyStructure, clampToSrgb, cleanFontValue, collectBrandPassthrough, collectDeclaredProperties, compositeOverBackground, declaresWeights, exportTheme, extractFromCSS, formatFontCoverageError, generateDarkCss, generateFontMetricDecls, generateFontWeightDecls, generateFullBundleCss, generateLightCss, generatePreloadLinks, generatePrimitives, generatePrimitivesCss, generateSemanticAliasDecls, generateSemanticCss, generateShadeScale, generateStylesheetLinks, generateTheme, generateThemeData, generateThemeDataFromConfig, generateThemeFromConfig, getContrastRatio, getKnownTokenRefs, getLuminance, googleFontsCatalog, hasBrandPassthrough, hexToOklch, hexToRgb, isValidColor, isValidHex, isVisorThemeConfig, loadedWeights, lookupFontWeightAlias, lookupGoogleFont, matchLoadedWeight, normalizeHex, oklchToHex, parseCSSDeclarations, parseColor, parseConfig, parseFontFaceDeclarations, parseHex, parseHsla, parseOklch, parseRgba, resolveBrandSlot, resolveBrandSource, resolveConfig, resolveFont, resolveRoleWeight, resolveThemeBrand, resolveThemeFonts, resolveWeightRamp, rgbToHex, serializeBrandStrategy, serializeColor, validate, validateBrandStrategy, validateConfig, validateFontCoverage, visorTheme_schema as visorThemeSchema };
2443
+ export { type BrandPassthrough, BrandResolution, BrandSlot, BrandSource, BrandStrategy, BrandStrategyContext, BrandStrategyIssue, BrandStrategyValidationResult, type CSSFile, ColorRole, type Confidence, DEFAULT_VISOR_BRAND, type ExtractedToken, type ExtractionResult, FONT_VERTICAL_METRICS, FONT_WEIGHT_ALIASES, type FontCoverageError, type FontCoverageResult, FontDisplayStrategy, type FontFaceDeclaration, FontResolution, FontResolveOptions, type FontVerticalMetrics, FullShadeScale, GeneratedPrimitives, GoogleFontEntry, MODE_DEPENDENT_SEMANTIC_ALIASES, OKLCH, ParsedColor, RGB, ResolvedThemeConfig, type ResolvedWeightRamp, SEMANTIC_MAP, SelectiveShadeScale, SemanticTokens, SerializedBrandStrategy, ShadeStep, TAILWIND_GRAY, ThemeBrandResult, ThemeData, ThemeFontResult, ThemeOutput, type ThemeValidationResult, VISOR_BRANDS_CDN, VISOR_CORE_DARK_SEMANTIC_ALIASES, VISOR_CORE_SEMANTIC_ALIASES, VISOR_DEFAULT_BRAND_PATH, VISOR_FONTS_CDN, type ValidateOptions, type ValidationIssue, type ValidationSeverity, VisorBrand, VisorThemeConfig, VisorTypography, WEIGHT_RAMP_TARGETS, applyOverrides, assignSemanticTokens, buildVisorBrandUrl, buildVisorFontUrl, checkBrandStrategyCoherence, checkBrandStrategyStructure, clampToSrgb, cleanFontValue, collectBrandPassthrough, collectDeclaredProperties, compositeOverBackground, declaresWeights, exportTheme, extractFromCSS, formatFontCoverageError, generateDarkCss, generateFontMetricDecls, generateFontWeightDecls, generateFullBundleCss, generateLightCss, generatePreloadLinks, generatePrimitives, generatePrimitivesCss, generateSemanticAliasDecls, generateSemanticCss, generateShadeScale, generateStylesheetLinks, generateTheme, generateThemeData, generateThemeDataFromConfig, generateThemeFromConfig, getContrastRatio, getKnownTokenRefs, getLuminance, googleFontsCatalog, hasBrandPassthrough, hexToOklch, hexToRgb, isValidColor, isValidHex, isVisorThemeConfig, loadedWeights, lookupFontWeightAlias, lookupGoogleFont, matchLoadedWeight, normalizeHex, oklchToHex, parseCSSDeclarations, parseColor, parseConfig, parseFontFaceDeclarations, parseHex, parseHsla, parseOklch, parseRgba, resolveBrandSlot, resolveBrandSource, resolveConfig, resolveFont, resolveRoleWeight, resolveThemeBrand, resolveThemeFonts, resolveWeightRamp, rgbToHex, serializeBrandStrategy, serializeColor, validate, validateBrandStrategy, validateConfig, validateFontCoverage, visorTheme_schema as visorThemeSchema };
package/dist/index.js CHANGED
@@ -6,8 +6,11 @@ import {
6
6
  FONT_VERTICAL_METRICS,
7
7
  FONT_WEIGHT_ALIASES,
8
8
  MATERIAL_TEXT_SLOTS,
9
+ MODE_DEPENDENT_SEMANTIC_ALIASES,
9
10
  TAILWIND_GRAY,
11
+ TEXT_TRANSFORMS,
10
12
  VISOR_BRANDS_CDN,
13
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
11
14
  VISOR_CORE_SEMANTIC_ALIASES,
12
15
  VISOR_DEFAULT_BRAND_PATH,
13
16
  VISOR_FONTS_CDN,
@@ -65,7 +68,7 @@ import {
65
68
  rgbToOklch,
66
69
  serializeColor,
67
70
  validateComponentBindings
68
- } from "./chunk-X2SWJPM2.js";
71
+ } from "./chunk-YMCDUC7N.js";
69
72
 
70
73
  // src/fonts/validate-coverage.ts
71
74
  var FONT_VAR_RE = /--font-(heading|display|body|sans|mono)\s*:\s*([^;]+);/g;
@@ -749,6 +752,11 @@ var visor_theme_schema_default = {
749
752
  org: {
750
753
  type: "string",
751
754
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
755
+ },
756
+ "text-transform": {
757
+ type: "string",
758
+ enum: ["none", "uppercase", "lowercase", "capitalize"],
759
+ description: "Casing applied to this slot's text via --font-heading-text-transform. Emitted only when set."
752
760
  }
753
761
  }
754
762
  },
@@ -775,6 +783,11 @@ var visor_theme_schema_default = {
775
783
  org: {
776
784
  type: "string",
777
785
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
786
+ },
787
+ "text-transform": {
788
+ type: "string",
789
+ enum: ["none", "uppercase", "lowercase", "capitalize"],
790
+ description: "Casing applied to this slot's text via --font-display-text-transform. Emitted only when set."
778
791
  }
779
792
  }
780
793
  },
@@ -800,6 +813,11 @@ var visor_theme_schema_default = {
800
813
  org: {
801
814
  type: "string",
802
815
  description: "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
816
+ },
817
+ "text-transform": {
818
+ type: "string",
819
+ enum: ["none", "uppercase", "lowercase", "capitalize"],
820
+ description: "Casing applied to this slot's text via --font-body-text-transform. Emitted only when set."
803
821
  }
804
822
  }
805
823
  },
@@ -810,6 +828,11 @@ var visor_theme_schema_default = {
810
828
  family: {
811
829
  type: "string",
812
830
  description: "Google Fonts family name or CSS font stack for monospace."
831
+ },
832
+ "text-transform": {
833
+ type: "string",
834
+ enum: ["none", "uppercase", "lowercase", "capitalize"],
835
+ description: "Casing applied to this slot's text via --font-mono-text-transform. Emitted only when set."
813
836
  }
814
837
  }
815
838
  },
@@ -1254,7 +1277,7 @@ var visor_theme_schema_default = {
1254
1277
  },
1255
1278
  switch: {
1256
1279
  type: "object",
1257
- description: 'Switch track and knob dimensions (default size; `size="sm"` keeps its own). Set `knob-inset` to size the knob from the track: it sits that far from the track\'s outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color.',
1280
+ description: 'Switch track and knob dimensions (default size; `size="sm"` keeps its own) and colours. Set `knob-inset` to size the knob from the track: it sits that far from the track\'s outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color, track-hover-bg, track-hover-bg-checked, knob-bg, knob-bg-checked.',
1258
1281
  additionalProperties: {
1259
1282
  $ref: "#/$defs/componentTokenBinding"
1260
1283
  }
@@ -1615,8 +1638,8 @@ var KNOWN_TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
1615
1638
  "text-on-dark"
1616
1639
  ]);
1617
1640
  var KNOWN_CDN_OVERRIDE_KEYS = /* @__PURE__ */ new Set(["visor-fonts"]);
1618
- var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
1619
- var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org"]);
1641
+ var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org", "text-transform"]);
1642
+ var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org", "text-transform"]);
1620
1643
  var KNOWN_LETTER_SPACING_KEYS = /* @__PURE__ */ new Set([
1621
1644
  "xl",
1622
1645
  "lg",
@@ -1933,6 +1956,12 @@ function validateConfig(config) {
1933
1956
  errors.push(`'typography.cdn-overrides.visor-fonts' must not be empty`);
1934
1957
  }
1935
1958
  const orgOptional = typeof visorFontsOverride === "string" && visorFontsOverride.length > 0;
1959
+ for (const slot of ["heading", "display", "body", "mono"]) {
1960
+ const tt = typo[slot]?.["text-transform"];
1961
+ if (tt !== void 0 && !TEXT_TRANSFORMS.includes(tt)) {
1962
+ errors.push(`'typography.${slot}.text-transform' must be one of: ${TEXT_TRANSFORMS.join(", ")}`);
1963
+ }
1964
+ }
1936
1965
  for (const slot of ["heading", "display", "body"]) {
1937
1966
  const font = typo[slot];
1938
1967
  if (font && font.source === "visor-fonts" && !orgOptional && !font.org) {
@@ -2187,6 +2216,7 @@ function resolveConfig(config) {
2187
2216
  weight: config.typography?.heading?.weight ?? DEFAULTS.typography.heading.weight,
2188
2217
  ...config.typography?.heading?.source && { source: config.typography.heading.source },
2189
2218
  ...config.typography?.heading?.org && { org: config.typography.heading.org },
2219
+ ...config.typography?.heading?.["text-transform"] && { "text-transform": config.typography.heading["text-transform"] },
2190
2220
  ...config.typography?.heading?.weights && { weights: config.typography.heading.weights }
2191
2221
  },
2192
2222
  display: {
@@ -2194,6 +2224,7 @@ function resolveConfig(config) {
2194
2224
  weight: config.typography?.display?.weight ?? 400,
2195
2225
  ...config.typography?.display?.source && { source: config.typography.display.source },
2196
2226
  ...config.typography?.display?.org && { org: config.typography.display.org },
2227
+ ...config.typography?.display?.["text-transform"] && { "text-transform": config.typography.display["text-transform"] },
2197
2228
  ...config.typography?.display?.weights && { weights: config.typography.display.weights }
2198
2229
  },
2199
2230
  body: {
@@ -2201,6 +2232,7 @@ function resolveConfig(config) {
2201
2232
  weight: config.typography?.body?.weight ?? DEFAULTS.typography.body.weight,
2202
2233
  ...config.typography?.body?.source && { source: config.typography.body.source },
2203
2234
  ...config.typography?.body?.org && { org: config.typography.body.org },
2235
+ ...config.typography?.body?.["text-transform"] && { "text-transform": config.typography.body["text-transform"] },
2204
2236
  ...config.typography?.body?.weights && { weights: config.typography.body.weights }
2205
2237
  },
2206
2238
  mono: {
@@ -2208,7 +2240,8 @@ function resolveConfig(config) {
2208
2240
  ...config.typography?.mono?.weight && { weight: config.typography.mono.weight },
2209
2241
  ...config.typography?.mono?.weights && { weights: config.typography.mono.weights },
2210
2242
  ...config.typography?.mono?.source && { source: config.typography.mono.source },
2211
- ...config.typography?.mono?.org && { org: config.typography.mono.org }
2243
+ ...config.typography?.mono?.org && { org: config.typography.mono.org },
2244
+ ...config.typography?.mono?.["text-transform"] && { "text-transform": config.typography.mono["text-transform"] }
2212
2245
  },
2213
2246
  ...config.typography?.["cdn-overrides"] && {
2214
2247
  "cdn-overrides": config.typography["cdn-overrides"]
@@ -4407,9 +4440,11 @@ export {
4407
4440
  FONT_VERTICAL_METRICS,
4408
4441
  FONT_WEIGHT_ALIASES,
4409
4442
  GOVERNS_WILDCARD,
4443
+ MODE_DEPENDENT_SEMANTIC_ALIASES,
4410
4444
  SEMANTIC_MAP,
4411
4445
  TAILWIND_GRAY,
4412
4446
  VISOR_BRANDS_CDN,
4447
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
4413
4448
  VISOR_CORE_SEMANTIC_ALIASES,
4414
4449
  VISOR_DEFAULT_BRAND_PATH,
4415
4450
  VISOR_FONTS_CDN,
@@ -603,6 +603,8 @@ declare function validateComponentBindings(bindings: unknown): string[];
603
603
  * Covers the full pipeline: .visor.yaml config → shade scales → semantic tokens → CSS output.
604
604
  */
605
605
 
606
+ /** VI-687: casing a typography slot applies to its text. */
607
+ type TextTransform = "none" | "uppercase" | "lowercase" | "capitalize";
606
608
  type ShadeStep = 50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950;
607
609
  type ColorRole = "primary" | "accent" | "neutral" | "success" | "warning" | "error" | "info";
608
610
  type FullShadeScale = Record<ShadeStep, string>;
@@ -707,6 +709,7 @@ interface VisorThemeConfig {
707
709
  weights?: number[];
708
710
  source?: FontSource;
709
711
  org?: string;
712
+ "text-transform"?: TextTransform;
710
713
  };
711
714
  display?: {
712
715
  family?: string;
@@ -714,6 +717,7 @@ interface VisorThemeConfig {
714
717
  weights?: number[];
715
718
  source?: FontSource;
716
719
  org?: string;
720
+ "text-transform"?: TextTransform;
717
721
  };
718
722
  body?: {
719
723
  family?: string;
@@ -721,6 +725,7 @@ interface VisorThemeConfig {
721
725
  weights?: number[];
722
726
  source?: FontSource;
723
727
  org?: string;
728
+ "text-transform"?: TextTransform;
724
729
  };
725
730
  mono?: {
726
731
  family?: string;
@@ -728,6 +733,7 @@ interface VisorThemeConfig {
728
733
  weights?: number[];
729
734
  source?: FontSource;
730
735
  org?: string;
736
+ "text-transform"?: TextTransform;
731
737
  };
732
738
  /**
733
739
  * VI-447: 6-tier letter-spacing ramp (`xl | lg | md | sm | xs | tight`).
@@ -912,6 +918,7 @@ interface ResolvedThemeConfig {
912
918
  weights?: number[];
913
919
  source?: FontSource;
914
920
  org?: string;
921
+ "text-transform"?: TextTransform;
915
922
  };
916
923
  display: {
917
924
  family: string;
@@ -919,6 +926,7 @@ interface ResolvedThemeConfig {
919
926
  weights?: number[];
920
927
  source?: FontSource;
921
928
  org?: string;
929
+ "text-transform"?: TextTransform;
922
930
  };
923
931
  body: {
924
932
  family: string;
@@ -926,6 +934,7 @@ interface ResolvedThemeConfig {
926
934
  weights?: number[];
927
935
  source?: FontSource;
928
936
  org?: string;
937
+ "text-transform"?: TextTransform;
929
938
  };
930
939
  mono: {
931
940
  family: string;
@@ -933,6 +942,7 @@ interface ResolvedThemeConfig {
933
942
  weights?: number[];
934
943
  source?: FontSource;
935
944
  org?: string;
945
+ "text-transform"?: TextTransform;
936
946
  };
937
947
  /**
938
948
  * Per-source CDN base URL overrides (e.g., `visor-fonts:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loworbitstudio/visor-theme-engine",
3
- "version": "0.28.0",
3
+ "version": "0.29.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",
@@ -131,6 +131,11 @@
131
131
  "org": {
132
132
  "type": "string",
133
133
  "description": "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
134
+ },
135
+ "text-transform": {
136
+ "type": "string",
137
+ "enum": ["none", "uppercase", "lowercase", "capitalize"],
138
+ "description": "Casing applied to this slot's text via --font-heading-text-transform. Emitted only when set."
134
139
  }
135
140
  }
136
141
  },
@@ -157,6 +162,11 @@
157
162
  "org": {
158
163
  "type": "string",
159
164
  "description": "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
165
+ },
166
+ "text-transform": {
167
+ "type": "string",
168
+ "enum": ["none", "uppercase", "lowercase", "capitalize"],
169
+ "description": "Casing applied to this slot's text via --font-display-text-transform. Emitted only when set."
160
170
  }
161
171
  }
162
172
  },
@@ -182,6 +192,11 @@
182
192
  "org": {
183
193
  "type": "string",
184
194
  "description": "Organization namespace for visor-fonts CDN (required when source is visor-fonts)."
195
+ },
196
+ "text-transform": {
197
+ "type": "string",
198
+ "enum": ["none", "uppercase", "lowercase", "capitalize"],
199
+ "description": "Casing applied to this slot's text via --font-body-text-transform. Emitted only when set."
185
200
  }
186
201
  }
187
202
  },
@@ -192,6 +207,11 @@
192
207
  "family": {
193
208
  "type": "string",
194
209
  "description": "Google Fonts family name or CSS font stack for monospace."
210
+ },
211
+ "text-transform": {
212
+ "type": "string",
213
+ "enum": ["none", "uppercase", "lowercase", "capitalize"],
214
+ "description": "Casing applied to this slot's text via --font-mono-text-transform. Emitted only when set."
195
215
  }
196
216
  }
197
217
  },
@@ -636,7 +656,7 @@
636
656
  },
637
657
  "switch": {
638
658
  "type": "object",
639
- "description": "Switch track and knob dimensions (default size; `size=\"sm\"` keeps its own). Set `knob-inset` to size the knob from the track: it sits that far from the track's outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color.",
659
+ "description": "Switch track and knob dimensions (default size; `size=\"sm\"` keeps its own) and colours. Set `knob-inset` to size the knob from the track: it sits that far from the track's outer edge on every side. Keys: track-width, track-height, knob-inset, track-bg, edge-color, track-hover-bg, track-hover-bg-checked, knob-bg, knob-bg-checked.",
640
660
  "additionalProperties": {
641
661
  "$ref": "#/$defs/componentTokenBinding"
642
662
  }