@loworbitstudio/visor-theme-engine 0.28.0 → 0.30.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-4CCJNJAS.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());
@@ -1262,6 +1262,7 @@ var SPINNER = "components/ui/spinner/spinner.module.css";
1262
1262
  var CHECKBOX = "components/ui/checkbox/checkbox.module.css";
1263
1263
  var INPUT = "components/ui/input/input.module.css";
1264
1264
  var SEPARATOR = "components/ui/separator/separator.module.css";
1265
+ var ALERT = "components/ui/alert/alert.module.css";
1265
1266
  var NUMBER_INPUT = "components/ui/number-input/number-input.module.css";
1266
1267
  var TEXTAREA = "components/ui/textarea/textarea.module.css";
1267
1268
  var SELECT = "components/ui/select/select.module.css";
@@ -1988,7 +1989,7 @@ var textFamily = {
1988
1989
  var switchFamily = {
1989
1990
  family: "switch",
1990
1991
  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.',
1992
+ 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
1993
  tokens: [
1993
1994
  { key: "track-width", property: "width", description: "Track width.", consumers: at(SWITCH, "2rem") },
1994
1995
  {
@@ -2009,6 +2010,30 @@ var switchFamily = {
2009
2010
  property: "outline-color",
2010
2011
  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
2012
  consumers: at(SWITCH, "transparent")
2013
+ },
2014
+ {
2015
+ key: "track-hover-bg",
2016
+ property: "background-color (hover)",
2017
+ description: "Unchecked track fill under the pointer. Bind it in a borderless theme, where `--border-strong` is transparent.",
2018
+ consumers: at(SWITCH, "var(--border-strong, #d1d5db)")
2019
+ },
2020
+ {
2021
+ key: "track-hover-bg-checked",
2022
+ property: "background-color (hover)",
2023
+ 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.",
2024
+ consumers: at(SWITCH, "var(--border-strong, #d1d5db)")
2025
+ },
2026
+ {
2027
+ key: "knob-bg",
2028
+ property: "background-color (knob)",
2029
+ description: "Unchecked knob fill. Bind it in a dark theme, where the page colour is a black knob on a dark track.",
2030
+ consumers: at(SWITCH, "var(--surface-page, #ffffff)")
2031
+ },
2032
+ {
2033
+ key: "knob-bg-checked",
2034
+ property: "background-color (knob)",
2035
+ description: "Checked knob fill. Independent of `knob-bg`, so a light unchecked knob can sit beside a dark knob on a bright checked track.",
2036
+ consumers: at(SWITCH, "var(--surface-page, #ffffff)")
2012
2037
  }
2013
2038
  ]
2014
2039
  };
@@ -2102,6 +2127,20 @@ var separatorFamily = {
2102
2127
  }
2103
2128
  ]
2104
2129
  };
2130
+ var alertFamily = {
2131
+ family: "alert",
2132
+ prefix: "alert",
2133
+ description: "Alert's soft appearance: the inline alert under a control or in a dialog \u2014 icon size, inset, gap, rounding and type.",
2134
+ tokens: [
2135
+ { key: "icon-size", property: "width / height / min-height", description: "Soft icon size for the destructive, warning and success tones; the content is at least this tall.", consumers: at(ALERT, "2rem") },
2136
+ { key: "icon-size-note", property: "width / height / min-height", description: "Soft icon size for the neutral note (`default` and `info`).", consumers: at(ALERT, "1.5rem") },
2137
+ { key: "soft-padding", property: "padding", description: "Soft alert inset.", consumers: at(ALERT, "var(--spacing-3, 0.75rem) calc(var(--spacing-3, 0.75rem) + var(--spacing-1, 0.25rem) / 2)") },
2138
+ { key: "gap", property: "gap", description: "Gap between the soft icon and its text.", consumers: at(ALERT, "var(--spacing-3, 0.75rem)") },
2139
+ { key: "radius", property: "border-radius", description: "Soft alert corner rounding.", consumers: at(ALERT, "var(--radius-lg, 0.5rem)") },
2140
+ { key: "font-size", property: "font-size", description: "Soft alert type size.", consumers: at(ALERT, "var(--font-size-sm, 0.875rem)") },
2141
+ { key: "lead-font-weight", property: "font-weight", description: "Weight of the inline `AlertLead` sentence.", consumers: at(ALERT, "var(--font-weight-bold, 700)") }
2142
+ ]
2143
+ };
2105
2144
  var HAIRLINE_PX = [
2106
2145
  "components/ui/bulk-action-bar/bulk-action-bar.module.css",
2107
2146
  "components/ui/dialog/dialog.module.css",
@@ -2208,6 +2247,7 @@ var COMPONENT_TOKEN_FAMILIES = [
2208
2247
  actionRowFamily,
2209
2248
  inputAffixFamily,
2210
2249
  separatorFamily,
2250
+ alertFamily,
2211
2251
  adminUiFamily
2212
2252
  ];
2213
2253
  var COMPONENT_TOKEN_FAMILY_BY_NAME = new Map(
@@ -2720,6 +2760,14 @@ function generateFontSizeDecls(scale) {
2720
2760
  return `--font-size-${name}: ${rem}rem; /* ${Number(scaled.toFixed(2))}px */`;
2721
2761
  });
2722
2762
  }
2763
+ function generateFontTextTransformDecls(typography) {
2764
+ const decls = [];
2765
+ for (const slot of ["heading", "display", "body", "mono"]) {
2766
+ const value = typography[slot]["text-transform"];
2767
+ if (value) decls.push(`--font-${slot}-text-transform: ${value};`);
2768
+ }
2769
+ return decls;
2770
+ }
2723
2771
  function generateTypographyPrimitives(config, aliases = EMPTY_ALIASES) {
2724
2772
  const decls = [];
2725
2773
  decls.push(`--font-heading: ${fontStack(config.typography.heading.family, aliases)};`);
@@ -2730,6 +2778,7 @@ function generateTypographyPrimitives(config, aliases = EMPTY_ALIASES) {
2730
2778
  decls.push(...generateFontSizeDecls(config.typography.scale));
2731
2779
  decls.push(...generateFontMetricDecls(config.typography.body.family));
2732
2780
  decls.push(...generateFontWeightDecls(config.typography));
2781
+ decls.push(...generateFontTextTransformDecls(config.typography));
2733
2782
  const lineHeights = {
2734
2783
  none: 1,
2735
2784
  tight: 1.25,
@@ -3134,16 +3183,46 @@ var VISOR_CORE_SEMANTIC_ALIASES = {
3134
3183
  // Field-attached floating panels (VI-497).
3135
3184
  "field-menu-bg": "surface-popover"
3136
3185
  };
3137
- function collectDeclaredProperties(css) {
3186
+ var VISOR_CORE_DARK_SEMANTIC_ALIASES = {
3187
+ "border-input": "color-neutral-700",
3188
+ "skeleton-from": "color-neutral-800",
3189
+ "skeleton-to": "color-neutral-700",
3190
+ "chart-1": "color-primary-400",
3191
+ "sidebar-bg": "color-neutral-900",
3192
+ "sidebar-text": "color-neutral-300",
3193
+ "sidebar-primary-bg": "color-primary-500",
3194
+ "sidebar-accent-bg": "color-neutral-800",
3195
+ "sidebar-accent-text": "color-neutral-50",
3196
+ "sidebar-border": "color-neutral-700",
3197
+ "sidebar-ring": "color-primary-400",
3198
+ "sidebar-text-muted": "color-neutral-400"
3199
+ };
3200
+ var MODE_DEPENDENT_SEMANTIC_ALIASES = new Set(
3201
+ Object.keys(VISOR_CORE_DARK_SEMANTIC_ALIASES)
3202
+ );
3203
+ var VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST = {
3204
+ ...VISOR_CORE_SEMANTIC_ALIASES,
3205
+ ...VISOR_CORE_DARK_SEMANTIC_ALIASES
3206
+ };
3207
+ function collectDeclaredProperties(css, options = {}) {
3138
3208
  const declared = /* @__PURE__ */ new Set();
3139
- for (const match of css.matchAll(/(^|[{;\s])--([a-zA-Z0-9-]+)\s*:/g)) {
3140
- declared.add(match[2]);
3209
+ if (!options.exclude?.length) {
3210
+ for (const match of css.matchAll(/(^|[{;\s])--([a-zA-Z0-9-]+)\s*:/g)) {
3211
+ declared.add(match[2]);
3212
+ }
3213
+ return declared;
3214
+ }
3215
+ const exclude = new Set(options.exclude);
3216
+ for (const rule of css.replace(/\/\*[\s\S]*?\*\//g, "").matchAll(/([^{}]*)\{([^{}]*)\}/g)) {
3217
+ if (exclude.has(rule[1].trim())) continue;
3218
+ for (const prop of collectDeclaredProperties(rule[2])) declared.add(prop);
3141
3219
  }
3142
3220
  return declared;
3143
3221
  }
3144
- function generateSemanticAliasDecls(declared) {
3222
+ function generateSemanticAliasDecls(declared, options = {}) {
3145
3223
  const decls = [];
3146
- for (const [alias, referent] of Object.entries(VISOR_CORE_SEMANTIC_ALIASES)) {
3224
+ for (const [alias, referent] of Object.entries(options.table ?? VISOR_CORE_SEMANTIC_ALIASES)) {
3225
+ if (options.skip?.has(alias)) continue;
3147
3226
  if (declared.has(alias)) continue;
3148
3227
  if (!declared.has(referent)) continue;
3149
3228
  decls.push(`--${alias}: var(--${referent});`);
@@ -3152,6 +3231,7 @@ function generateSemanticAliasDecls(declared) {
3152
3231
  }
3153
3232
 
3154
3233
  // src/types.ts
3234
+ var TEXT_TRANSFORMS = ["none", "uppercase", "lowercase", "capitalize"];
3155
3235
  var MATERIAL_TEXT_SLOTS = [
3156
3236
  "displayLarge",
3157
3237
  "displayMedium",
@@ -3208,6 +3288,7 @@ export {
3208
3288
  isValidColor,
3209
3289
  compositeOverBackground,
3210
3290
  serializeColor,
3291
+ TEXT_TRANSFORMS,
3211
3292
  MATERIAL_TEXT_SLOTS,
3212
3293
  componentTokenName,
3213
3294
  COMPONENT_TOKEN_FAMILIES,
@@ -3238,6 +3319,7 @@ export {
3238
3319
  sectionComment,
3239
3320
  block2 as block,
3240
3321
  generateFontSizeDecls,
3322
+ generateFontTextTransformDecls,
3241
3323
  generatePrimitivesCss,
3242
3324
  generateSemanticCss,
3243
3325
  generateTextScaleAliasDecls,
@@ -3248,6 +3330,9 @@ export {
3248
3330
  generateDarkCss,
3249
3331
  generateFullBundleCss,
3250
3332
  VISOR_CORE_SEMANTIC_ALIASES,
3333
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
3334
+ MODE_DEPENDENT_SEMANTIC_ALIASES,
3335
+ VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST,
3251
3336
  collectDeclaredProperties,
3252
3337
  generateSemanticAliasDecls
3253
3338
  };
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
  }
@@ -1263,6 +1303,13 @@ var properties = {
1263
1303
  $ref: "#/$defs/componentTokenBinding"
1264
1304
  }
1265
1305
  },
1306
+ alert: {
1307
+ type: "object",
1308
+ description: "Alert's soft appearance: the inline alert under a control or in a dialog — icon size, inset, gap, rounding and type. Keys: icon-size, icon-size-note, soft-padding, gap, radius, font-size, lead-font-weight.",
1309
+ additionalProperties: {
1310
+ $ref: "#/$defs/componentTokenBinding"
1311
+ }
1312
+ },
1266
1313
  "admin-ui": {
1267
1314
  type: "object",
1268
1315
  description: "Structural roles the admin-ui pattern owns. `marquee-family` is the display face for KPI hero figures and marquee page titles — the role the admin-ui portability audit named as the one piece of ENTR brand coupling a new theme must rebind. Keys: marquee-family.",
@@ -2054,6 +2101,25 @@ declare function getKnownTokenRefs(): Set<string>;
2054
2101
  * a diff against that file readable.
2055
2102
  */
2056
2103
  declare const VISOR_CORE_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2104
+ /**
2105
+ * Aliases visor-core declares with a **different referent in dark mode**, mapped
2106
+ * to that dark referent. `--sidebar-bg` is `var(--color-neutral-50)` on `:root`
2107
+ * but `var(--color-neutral-900)` under `[data-theme="dark"]` and the
2108
+ * prefers-color-scheme block.
2109
+ *
2110
+ * `VISOR_CORE_SEMANTIC_ALIASES` records the light referent. A theme scope
2111
+ * re-declares these with the light referent on the host and with the dark one
2112
+ * on its dark selectors (VI-696), so a scoped dark sidebar resolves to the
2113
+ * theme's own `neutral-900`, not its near-white `neutral-50` (the VI-648 flat
2114
+ * emission) nor visor-core's untuned default (the VI-695 skip).
2115
+ *
2116
+ * Pinned against the dark rules of the emitted `tokens.css` by
2117
+ * `semantic-alias-coverage`: both the key set and every referent. A new
2118
+ * mode-dependent alias, or a changed dark referent, fails there by name.
2119
+ */
2120
+ declare const VISOR_CORE_DARK_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2121
+ /** The aliases in `VISOR_CORE_DARK_SEMANTIC_ALIASES`: the ones whose referent changes with the mode. */
2122
+ declare const MODE_DEPENDENT_SEMANTIC_ALIASES: ReadonlySet<string>;
2057
2123
  /**
2058
2124
  * Every custom property declared anywhere in `css`, without the leading `--`.
2059
2125
  *
@@ -2061,8 +2127,13 @@ declare const VISOR_CORE_SEMANTIC_ALIASES: Readonly<Record<string, string>>;
2061
2127
  * `generateSemanticAliasDecls` reflects what this theme *actually* emits —
2062
2128
  * including conditional output such as the `--font-weight-<n>` ladder, which
2063
2129
  * only appears when a theme declares an explicit `weights` array.
2130
+ *
2131
+ * `options.exclude` drops the rules whose selector is listed, for a caller that
2132
+ * needs only what one mode's selector can see (VI-696).
2064
2133
  */
2065
- declare function collectDeclaredProperties(css: string): Set<string>;
2134
+ declare function collectDeclaredProperties(css: string, options?: {
2135
+ exclude?: readonly string[];
2136
+ }): Set<string>;
2066
2137
  /**
2067
2138
  * The `--alias: var(--referent);` declarations to re-emit on a theme's scope
2068
2139
  * selector, given everything the theme has emitted so far.
@@ -2079,11 +2150,20 @@ declare function collectDeclaredProperties(css: string): Set<string>;
2079
2150
  * the correct outcome — re-emitting would replace a working value with an
2080
2151
  * invalid one.
2081
2152
  *
2153
+ * `options.skip` names aliases to leave inheriting visor-core's value.
2154
+ *
2155
+ * `options.table` swaps the alias → referent table. A dark selector passes
2156
+ * `VISOR_CORE_DARK_SEMANTIC_ALIASES`, with `declared` read off only what that
2157
+ * selector sees; a dark-only host passes `VISOR_CORE_SEMANTIC_ALIASES_DARK_HOST`.
2158
+ *
2082
2159
  * Returns `[]` for a `:root`-scoped theme's caller, which does not need this at
2083
2160
  * all: there, visor-core's aliases and the theme's primitives are declared on
2084
2161
  * the same element, so substitution already sees the theme's values.
2085
2162
  */
2086
- declare function generateSemanticAliasDecls(declared: ReadonlySet<string>): string[];
2163
+ declare function generateSemanticAliasDecls(declared: ReadonlySet<string>, options?: {
2164
+ skip?: ReadonlySet<string>;
2165
+ table?: Readonly<Record<string, string>>;
2166
+ }): string[];
2087
2167
 
2088
2168
  /**
2089
2169
  * Per-theme font-family aliasing — substrate fix for VI-354.
@@ -2367,4 +2447,4 @@ declare const FONT_VERTICAL_METRICS: Record<string, FontVerticalMetrics>;
2367
2447
  /** The `--font-ascent` / `--font-descent` declarations for a body family, or none when the face is unmeasured. */
2368
2448
  declare function generateFontMetricDecls(family: string): string[];
2369
2449
 
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 };
2450
+ 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-4CCJNJAS.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
  }
@@ -1301,6 +1324,13 @@ var visor_theme_schema_default = {
1301
1324
  $ref: "#/$defs/componentTokenBinding"
1302
1325
  }
1303
1326
  },
1327
+ alert: {
1328
+ type: "object",
1329
+ description: "Alert's soft appearance: the inline alert under a control or in a dialog \u2014 icon size, inset, gap, rounding and type. Keys: icon-size, icon-size-note, soft-padding, gap, radius, font-size, lead-font-weight.",
1330
+ additionalProperties: {
1331
+ $ref: "#/$defs/componentTokenBinding"
1332
+ }
1333
+ },
1304
1334
  "admin-ui": {
1305
1335
  type: "object",
1306
1336
  description: "Structural roles the admin-ui pattern owns. `marquee-family` is the display face for KPI hero figures and marquee page titles \u2014 the role the admin-ui portability audit named as the one piece of ENTR brand coupling a new theme must rebind. Keys: marquee-family.",
@@ -1615,8 +1645,8 @@ var KNOWN_TYPOGRAPHY_KEYS = /* @__PURE__ */ new Set([
1615
1645
  "text-on-dark"
1616
1646
  ]);
1617
1647
  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"]);
1648
+ var KNOWN_TYPOGRAPHY_FONT_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org", "text-transform"]);
1649
+ var KNOWN_TYPOGRAPHY_MONO_KEYS = /* @__PURE__ */ new Set(["family", "weight", "weights", "source", "org", "text-transform"]);
1620
1650
  var KNOWN_LETTER_SPACING_KEYS = /* @__PURE__ */ new Set([
1621
1651
  "xl",
1622
1652
  "lg",
@@ -1933,6 +1963,12 @@ function validateConfig(config) {
1933
1963
  errors.push(`'typography.cdn-overrides.visor-fonts' must not be empty`);
1934
1964
  }
1935
1965
  const orgOptional = typeof visorFontsOverride === "string" && visorFontsOverride.length > 0;
1966
+ for (const slot of ["heading", "display", "body", "mono"]) {
1967
+ const tt = typo[slot]?.["text-transform"];
1968
+ if (tt !== void 0 && !TEXT_TRANSFORMS.includes(tt)) {
1969
+ errors.push(`'typography.${slot}.text-transform' must be one of: ${TEXT_TRANSFORMS.join(", ")}`);
1970
+ }
1971
+ }
1936
1972
  for (const slot of ["heading", "display", "body"]) {
1937
1973
  const font = typo[slot];
1938
1974
  if (font && font.source === "visor-fonts" && !orgOptional && !font.org) {
@@ -2187,6 +2223,7 @@ function resolveConfig(config) {
2187
2223
  weight: config.typography?.heading?.weight ?? DEFAULTS.typography.heading.weight,
2188
2224
  ...config.typography?.heading?.source && { source: config.typography.heading.source },
2189
2225
  ...config.typography?.heading?.org && { org: config.typography.heading.org },
2226
+ ...config.typography?.heading?.["text-transform"] && { "text-transform": config.typography.heading["text-transform"] },
2190
2227
  ...config.typography?.heading?.weights && { weights: config.typography.heading.weights }
2191
2228
  },
2192
2229
  display: {
@@ -2194,6 +2231,7 @@ function resolveConfig(config) {
2194
2231
  weight: config.typography?.display?.weight ?? 400,
2195
2232
  ...config.typography?.display?.source && { source: config.typography.display.source },
2196
2233
  ...config.typography?.display?.org && { org: config.typography.display.org },
2234
+ ...config.typography?.display?.["text-transform"] && { "text-transform": config.typography.display["text-transform"] },
2197
2235
  ...config.typography?.display?.weights && { weights: config.typography.display.weights }
2198
2236
  },
2199
2237
  body: {
@@ -2201,6 +2239,7 @@ function resolveConfig(config) {
2201
2239
  weight: config.typography?.body?.weight ?? DEFAULTS.typography.body.weight,
2202
2240
  ...config.typography?.body?.source && { source: config.typography.body.source },
2203
2241
  ...config.typography?.body?.org && { org: config.typography.body.org },
2242
+ ...config.typography?.body?.["text-transform"] && { "text-transform": config.typography.body["text-transform"] },
2204
2243
  ...config.typography?.body?.weights && { weights: config.typography.body.weights }
2205
2244
  },
2206
2245
  mono: {
@@ -2208,7 +2247,8 @@ function resolveConfig(config) {
2208
2247
  ...config.typography?.mono?.weight && { weight: config.typography.mono.weight },
2209
2248
  ...config.typography?.mono?.weights && { weights: config.typography.mono.weights },
2210
2249
  ...config.typography?.mono?.source && { source: config.typography.mono.source },
2211
- ...config.typography?.mono?.org && { org: config.typography.mono.org }
2250
+ ...config.typography?.mono?.org && { org: config.typography.mono.org },
2251
+ ...config.typography?.mono?.["text-transform"] && { "text-transform": config.typography.mono["text-transform"] }
2212
2252
  },
2213
2253
  ...config.typography?.["cdn-overrides"] && {
2214
2254
  "cdn-overrides": config.typography["cdn-overrides"]
@@ -4407,9 +4447,11 @@ export {
4407
4447
  FONT_VERTICAL_METRICS,
4408
4448
  FONT_WEIGHT_ALIASES,
4409
4449
  GOVERNS_WILDCARD,
4450
+ MODE_DEPENDENT_SEMANTIC_ALIASES,
4410
4451
  SEMANTIC_MAP,
4411
4452
  TAILWIND_GRAY,
4412
4453
  VISOR_BRANDS_CDN,
4454
+ VISOR_CORE_DARK_SEMANTIC_ALIASES,
4413
4455
  VISOR_CORE_SEMANTIC_ALIASES,
4414
4456
  VISOR_DEFAULT_BRAND_PATH,
4415
4457
  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.30.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
  }
@@ -683,6 +703,13 @@
683
703
  "$ref": "#/$defs/componentTokenBinding"
684
704
  }
685
705
  },
706
+ "alert": {
707
+ "type": "object",
708
+ "description": "Alert's soft appearance: the inline alert under a control or in a dialog — icon size, inset, gap, rounding and type. Keys: icon-size, icon-size-note, soft-padding, gap, radius, font-size, lead-font-weight.",
709
+ "additionalProperties": {
710
+ "$ref": "#/$defs/componentTokenBinding"
711
+ }
712
+ },
686
713
  "admin-ui": {
687
714
  "type": "object",
688
715
  "description": "Structural roles the admin-ui pattern owns. `marquee-family` is the display face for KPI hero figures and marquee page titles — the role the admin-ui portability audit named as the one piece of ENTR brand coupling a new theme must rebind. Keys: marquee-family.",