@loworbitstudio/visor-theme-engine 0.16.0 → 0.17.1

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, r as SemanticTokens, R as ResolvedThemeConfig } from '../types-BstIS9rL.js';
1
+ import { k as GeneratedPrimitives, s as SemanticTokens, R as ResolvedThemeConfig } from '../types-Cvm7vFwe.js';
2
2
 
3
3
  /**
4
4
  * Adapter types for the Visor theme engine.
@@ -20,7 +20,7 @@ import {
20
20
  resolveThemeBrand,
21
21
  resolveThemeFonts,
22
22
  sectionComment
23
- } from "../chunk-DQ256PSE.js";
23
+ } from "../chunk-BUWBBUFG.js";
24
24
 
25
25
  // src/adapters/brand-passthrough.ts
26
26
  var SENTINEL_COLOR = "#ff00ff";
@@ -43,7 +43,7 @@ function indentBlock(selector, decls) {
43
43
  if (decls.length === 0) return "";
44
44
  return [selector + " {", ...decls.map((d) => ` ${d}`), "}"].join("\n");
45
45
  }
46
- function generateBrandPassthroughCss(passthrough, selectors) {
46
+ function generateBrandPassthroughCss(passthrough, selectors, colorScheme = "adaptive") {
47
47
  const lightKeys = Object.keys(passthrough.light);
48
48
  const darkKeys = Object.keys(passthrough.dark);
49
49
  if (lightKeys.length === 0 && darkKeys.length === 0) return "";
@@ -53,6 +53,20 @@ function generateBrandPassthroughCss(passthrough, selectors) {
53
53
  const count = lightKeys.length + darkKeys.length;
54
54
  blocks.push(`/* [visor-brand] ${count} passthrough: ${names} */`);
55
55
  }
56
+ if (colorScheme === "dark-only") {
57
+ if (darkKeys.length > 0) {
58
+ const decls = darkKeys.map((k) => declFor(k, passthrough.dark[k]));
59
+ blocks.push(indentBlock(selectors.light, decls));
60
+ }
61
+ return blocks.filter(Boolean).join("\n\n");
62
+ }
63
+ if (colorScheme === "light-only") {
64
+ if (lightKeys.length > 0) {
65
+ const decls = lightKeys.map((k) => declFor(k, passthrough.light[k]));
66
+ blocks.push(indentBlock(selectors.light, decls));
67
+ }
68
+ return blocks.filter(Boolean).join("\n\n");
69
+ }
56
70
  if (lightKeys.length > 0) {
57
71
  const decls = lightKeys.map((k) => declFor(k, passthrough.light[k]));
58
72
  blocks.push(indentBlock(selectors.light, decls));
@@ -86,6 +100,7 @@ function nextjsAdapter(input, options) {
86
100
  const includeFontImports = options?.includeFontImports ?? true;
87
101
  const includeFowt = options?.includeFowt ?? true;
88
102
  const scopePrefix = options?.scopePrefix;
103
+ const colorScheme = input.config["color-scheme"] ?? "adaptive";
89
104
  const lines = [];
90
105
  const slug = toKebabCase(input.config.name);
91
106
  const aliasedFamilies = /* @__PURE__ */ new Map();
@@ -156,7 +171,7 @@ function nextjsAdapter(input, options) {
156
171
  light: scopePrefix ?? ":root",
157
172
  dark: darkSelectors.join(",\n"),
158
173
  prefers: scopePrefix ? `${scopePrefix}:not(.light):not(.theme-light):not([data-theme="light"])` : ':root:not(.light):not(.theme-light):not([data-theme="light"])'
159
- });
174
+ }, colorScheme);
160
175
  if (passthroughCss) {
161
176
  lines.push(wrapInLayer("visor-brand", passthroughCss));
162
177
  lines.push("");
@@ -173,43 +188,60 @@ function nextjsAdapter(input, options) {
173
188
  semanticLines.push(sectionComment("Discrete: Space aliases (--space-N)"));
174
189
  semanticLines.push(block(hostSelector, generateSpaceAliasDecls(input.config)));
175
190
  semanticLines.push("");
176
- semanticLines.push(sectionComment("Intent aliases (light)"));
177
- semanticLines.push(block(lightModeSelector, generateIntentDecls(input.tokens, "light")));
178
- semanticLines.push("");
179
- semanticLines.push(sectionComment("Hairline aliases (light)"));
180
- semanticLines.push(block(lightModeSelector, generateHairlineDecls(input.tokens, "light")));
181
- semanticLines.push("");
182
- semanticLines.push(sectionComment("Intent aliases (dark) \u2014 manual toggle"));
183
- semanticLines.push(block(darkModeSelector, generateIntentDecls(input.tokens, "dark")));
184
- semanticLines.push("");
185
- semanticLines.push(sectionComment("Hairline aliases (dark) \u2014 manual toggle"));
186
- semanticLines.push(block(darkModeSelector, generateHairlineDecls(input.tokens, "dark")));
187
- semanticLines.push("");
188
- semanticLines.push(sectionComment("Intent aliases (dark) \u2014 prefers-color-scheme"));
189
- {
190
- const inner = block(prefersSelector, generateIntentDecls(input.tokens, "dark"));
191
- semanticLines.push(`@media (prefers-color-scheme: dark) {
191
+ if (colorScheme === "dark-only") {
192
+ semanticLines.push(sectionComment("Intent aliases (dark) \u2014 host"));
193
+ semanticLines.push(block(hostSelector, generateIntentDecls(input.tokens, "dark")));
194
+ semanticLines.push("");
195
+ semanticLines.push(sectionComment("Hairline aliases (dark) \u2014 host"));
196
+ semanticLines.push(block(hostSelector, generateHairlineDecls(input.tokens, "dark")));
197
+ semanticLines.push("");
198
+ } else if (colorScheme === "light-only") {
199
+ semanticLines.push(sectionComment("Intent aliases (light) \u2014 host"));
200
+ semanticLines.push(block(hostSelector, generateIntentDecls(input.tokens, "light")));
201
+ semanticLines.push("");
202
+ semanticLines.push(sectionComment("Hairline aliases (light) \u2014 host"));
203
+ semanticLines.push(block(hostSelector, generateHairlineDecls(input.tokens, "light")));
204
+ semanticLines.push("");
205
+ } else {
206
+ semanticLines.push(sectionComment("Intent aliases (light)"));
207
+ semanticLines.push(block(lightModeSelector, generateIntentDecls(input.tokens, "light")));
208
+ semanticLines.push("");
209
+ semanticLines.push(sectionComment("Hairline aliases (light)"));
210
+ semanticLines.push(block(lightModeSelector, generateHairlineDecls(input.tokens, "light")));
211
+ semanticLines.push("");
212
+ semanticLines.push(sectionComment("Intent aliases (dark) \u2014 manual toggle"));
213
+ semanticLines.push(block(darkModeSelector, generateIntentDecls(input.tokens, "dark")));
214
+ semanticLines.push("");
215
+ semanticLines.push(sectionComment("Hairline aliases (dark) \u2014 manual toggle"));
216
+ semanticLines.push(block(darkModeSelector, generateHairlineDecls(input.tokens, "dark")));
217
+ semanticLines.push("");
218
+ semanticLines.push(sectionComment("Intent aliases (dark) \u2014 prefers-color-scheme"));
219
+ {
220
+ const inner = block(prefersSelector, generateIntentDecls(input.tokens, "dark"));
221
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
192
222
  ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
193
223
  }`);
194
- }
195
- semanticLines.push("");
196
- semanticLines.push(sectionComment("Hairline aliases (dark) \u2014 prefers-color-scheme"));
197
- {
198
- const inner = block(prefersSelector, generateHairlineDecls(input.tokens, "dark"));
199
- semanticLines.push(`@media (prefers-color-scheme: dark) {
224
+ }
225
+ semanticLines.push("");
226
+ semanticLines.push(sectionComment("Hairline aliases (dark) \u2014 prefers-color-scheme"));
227
+ {
228
+ const inner = block(prefersSelector, generateHairlineDecls(input.tokens, "dark"));
229
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
200
230
  ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
201
231
  }`);
232
+ }
233
+ semanticLines.push("");
202
234
  }
203
- semanticLines.push("");
204
235
  const semanticLayer = wrapInLayer("visor-semantic", semanticLines.join("\n").trim());
205
236
  if (semanticLayer) {
206
237
  lines.push(semanticLayer);
207
238
  lines.push("");
208
239
  }
209
- const lightBody = stripHeader(generateLightCss(input.tokens, { scopePrefix }));
210
- const darkBody = stripHeader(generateDarkCss(input.tokens, { scopePrefix }));
240
+ const lightBody = stripHeader(generateLightCss(input.tokens, { scopePrefix, colorScheme }));
241
+ const darkBody = stripHeader(generateDarkCss(input.tokens, { scopePrefix, colorScheme }));
242
+ const adaptiveBody = [lightBody, darkBody].filter(Boolean).join("\n\n");
211
243
  lines.push(
212
- wrapInLayer("visor-adaptive", lightBody + "\n\n" + darkBody)
244
+ wrapInLayer("visor-adaptive", adaptiveBody)
213
245
  );
214
246
  if (includeFowt) {
215
247
  lines.push("");
@@ -554,6 +586,14 @@ function sectionComment2(label) {
554
586
  function docsAdapter(input, options) {
555
587
  const slug = toKebabCase3(input.config.name);
556
588
  const scopeClass = `.${slug}-theme`;
589
+ const colorScheme = input.config["color-scheme"] ?? "adaptive";
590
+ const singleMode = colorScheme === "dark-only" ? "dark" : colorScheme === "light-only" ? "light" : null;
591
+ const emitDark = colorScheme !== "light-only";
592
+ const emitLight = colorScheme !== "dark-only";
593
+ const emitPrefers = colorScheme === "adaptive";
594
+ const darkSel = singleMode ? scopeClass : `.dark ${scopeClass}`;
595
+ const lightSel = singleMode ? scopeClass : `html:not(.dark) ${scopeClass}`;
596
+ const darkSuffix = singleMode ? " \u2014 host" : " \u2014 manual toggle";
557
597
  const includeFontImports = options?.includeFontImports ?? true;
558
598
  const fontLines = [];
559
599
  const lines = [];
@@ -601,6 +641,8 @@ function docsAdapter(input, options) {
601
641
  const sharedDecls = [
602
642
  "min-height: 100vh;",
603
643
  "font-size: 1rem;",
644
+ // BO-56: pin UA chrome to the brand's single mode (adaptive emits nothing).
645
+ ...singleMode ? [`color-scheme: ${singleMode};`] : [],
604
646
  `background: var(--surface-page, var(--surface-background));`,
605
647
  "color: var(--text-primary);",
606
648
  "font-family: var(--font-sans);"
@@ -650,9 +692,9 @@ function docsAdapter(input, options) {
650
692
  }
651
693
  }
652
694
  }
653
- if (darkPrimitiveOverrides.length > 0) {
695
+ if (darkPrimitiveOverrides.length > 0 && emitDark) {
654
696
  lines.push(sectionComment2("Primitive overrides (dark) \u2014 dark brand color anchors at shade 500"));
655
- lines.push(block2(`.dark ${scopeClass}`, darkPrimitiveOverrides));
697
+ lines.push(block2(darkSel, darkPrimitiveOverrides));
656
698
  lines.push("");
657
699
  }
658
700
  const categories = ["Text", "Surface", "Border", "Interactive"];
@@ -662,10 +704,12 @@ function docsAdapter(input, options) {
662
704
  Object.entries(input.tokens.border).map(([n, v]) => `--border-${n}: ${v.dark};`),
663
705
  Object.entries(input.tokens.interactive).map(([n, v]) => `--interactive-${n}: ${v.dark};`)
664
706
  ];
665
- for (let i = 0; i < categories.length; i++) {
666
- lines.push(sectionComment2(`Adaptive: ${categories[i]} (dark) \u2014 manual toggle`));
667
- lines.push(block2(`.dark ${scopeClass}`, categoryDecls[i]));
668
- lines.push("");
707
+ if (emitDark) {
708
+ for (let i = 0; i < categories.length; i++) {
709
+ lines.push(sectionComment2(`Adaptive: ${categories[i]} (dark)${darkSuffix}`));
710
+ lines.push(block2(darkSel, categoryDecls[i]));
711
+ lines.push("");
712
+ }
669
713
  }
670
714
  const pcsCategories = [
671
715
  { label: "Text", entries: Object.entries(input.tokens.text).map(([n, v]) => `--text-${n}: ${v.dark};`) },
@@ -673,15 +717,17 @@ function docsAdapter(input, options) {
673
717
  { label: "Border", entries: Object.entries(input.tokens.border).map(([n, v]) => `--border-${n}: ${v.dark};`) },
674
718
  { label: "Interactive", entries: Object.entries(input.tokens.interactive).map(([n, v]) => `--interactive-${n}: ${v.dark};`) }
675
719
  ];
676
- for (const cat of pcsCategories) {
677
- lines.push(sectionComment2(`Adaptive: ${cat.label} (dark) \u2014 prefers-color-scheme`));
678
- const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, cat.entries);
679
- lines.push(`@media (prefers-color-scheme: dark) {
720
+ if (emitPrefers) {
721
+ for (const cat of pcsCategories) {
722
+ lines.push(sectionComment2(`Adaptive: ${cat.label} (dark) \u2014 prefers-color-scheme`));
723
+ const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, cat.entries);
724
+ lines.push(`@media (prefers-color-scheme: dark) {
680
725
  ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
681
726
  }`);
682
- lines.push("");
727
+ lines.push("");
728
+ }
683
729
  }
684
- if (darkPrimitiveOverrides.length > 0) {
730
+ if (darkPrimitiveOverrides.length > 0 && emitPrefers) {
685
731
  lines.push(sectionComment2("Primitive overrides (dark) \u2014 prefers-color-scheme"));
686
732
  const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, darkPrimitiveOverrides);
687
733
  lines.push(`@media (prefers-color-scheme: dark) {
@@ -696,17 +742,23 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
696
742
  { label: "Border", entries: Object.entries(input.tokens.border).map(([n, v]) => `--border-${n}: ${v.light};`) },
697
743
  { label: "Interactive", entries: Object.entries(input.tokens.interactive).map(([n, v]) => `--interactive-${n}: ${v.light};`) }
698
744
  ];
699
- for (const cat of lightCategoryDecls) {
700
- lines.push(sectionComment2(`Adaptive: ${cat.label} (light)`));
701
- lines.push(block2(`html:not(.dark) ${scopeClass}`, cat.entries));
745
+ if (emitLight) {
746
+ for (const cat of lightCategoryDecls) {
747
+ lines.push(sectionComment2(`Adaptive: ${cat.label} (light)`));
748
+ lines.push(block2(lightSel, cat.entries));
749
+ lines.push("");
750
+ }
751
+ }
752
+ if (emitDark) {
753
+ lines.push(sectionComment2("Fumadocs bridge: dark"));
754
+ lines.push(block2(darkSel, generateFumadocsBridgeDecls(input.tokens, "dark")));
755
+ lines.push("");
756
+ }
757
+ if (emitLight) {
758
+ lines.push(sectionComment2("Fumadocs bridge: light"));
759
+ lines.push(block2(lightSel, generateFumadocsBridgeDecls(input.tokens, "light")));
702
760
  lines.push("");
703
761
  }
704
- lines.push(sectionComment2("Fumadocs bridge: dark"));
705
- lines.push(block2(`.dark ${scopeClass}`, generateFumadocsBridgeDecls(input.tokens, "dark")));
706
- lines.push("");
707
- lines.push(sectionComment2("Fumadocs bridge: light"));
708
- lines.push(block2(`html:not(.dark) ${scopeClass}`, generateFumadocsBridgeDecls(input.tokens, "light")));
709
- lines.push("");
710
762
  const semanticLines = [];
711
763
  semanticLines.push("\n/* \u2500\u2500 Layer: Semantic aliases (VI-451) \u2500\u2500 */");
712
764
  semanticLines.push(sectionComment2("Discrete: Text size aliases (--text-N)"));
@@ -715,42 +767,49 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
715
767
  semanticLines.push(sectionComment2("Discrete: Space aliases (--space-N)"));
716
768
  semanticLines.push(block2(scopeClass, generateSpaceAliasDecls(input.config)));
717
769
  semanticLines.push("");
718
- semanticLines.push(sectionComment2("Intent aliases (light)"));
719
- semanticLines.push(block2(`html:not(.dark) ${scopeClass}`, generateIntentDecls(input.tokens, "light")));
720
- semanticLines.push("");
721
- semanticLines.push(sectionComment2("Hairline aliases (light)"));
722
- semanticLines.push(block2(`html:not(.dark) ${scopeClass}`, generateHairlineDecls(input.tokens, "light")));
723
- semanticLines.push("");
724
- semanticLines.push(sectionComment2("Intent aliases (dark) \u2014 manual toggle"));
725
- semanticLines.push(block2(`.dark ${scopeClass}`, generateIntentDecls(input.tokens, "dark")));
726
- semanticLines.push("");
727
- semanticLines.push(sectionComment2("Hairline aliases (dark) \u2014 manual toggle"));
728
- semanticLines.push(block2(`.dark ${scopeClass}`, generateHairlineDecls(input.tokens, "dark")));
729
- semanticLines.push("");
730
- semanticLines.push(sectionComment2("Intent aliases (dark) \u2014 prefers-color-scheme"));
731
- {
732
- const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, generateIntentDecls(input.tokens, "dark"));
733
- semanticLines.push(`@media (prefers-color-scheme: dark) {
770
+ if (emitLight) {
771
+ semanticLines.push(sectionComment2("Intent aliases (light)"));
772
+ semanticLines.push(block2(lightSel, generateIntentDecls(input.tokens, "light")));
773
+ semanticLines.push("");
774
+ semanticLines.push(sectionComment2("Hairline aliases (light)"));
775
+ semanticLines.push(block2(lightSel, generateHairlineDecls(input.tokens, "light")));
776
+ semanticLines.push("");
777
+ }
778
+ if (emitDark) {
779
+ semanticLines.push(sectionComment2(`Intent aliases (dark)${darkSuffix}`));
780
+ semanticLines.push(block2(darkSel, generateIntentDecls(input.tokens, "dark")));
781
+ semanticLines.push("");
782
+ semanticLines.push(sectionComment2(`Hairline aliases (dark)${darkSuffix}`));
783
+ semanticLines.push(block2(darkSel, generateHairlineDecls(input.tokens, "dark")));
784
+ semanticLines.push("");
785
+ }
786
+ if (emitPrefers) {
787
+ semanticLines.push(sectionComment2("Intent aliases (dark) \u2014 prefers-color-scheme"));
788
+ {
789
+ const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, generateIntentDecls(input.tokens, "dark"));
790
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
734
791
  ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
735
792
  }`);
736
- }
737
- semanticLines.push("");
738
- semanticLines.push(sectionComment2("Hairline aliases (dark) \u2014 prefers-color-scheme"));
739
- {
740
- const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, generateHairlineDecls(input.tokens, "dark"));
741
- semanticLines.push(`@media (prefers-color-scheme: dark) {
793
+ }
794
+ semanticLines.push("");
795
+ semanticLines.push(sectionComment2("Hairline aliases (dark) \u2014 prefers-color-scheme"));
796
+ {
797
+ const inner = block2(`${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`, generateHairlineDecls(input.tokens, "dark"));
798
+ semanticLines.push(`@media (prefers-color-scheme: dark) {
742
799
  ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
743
800
  }`);
801
+ }
802
+ semanticLines.push("");
744
803
  }
745
- semanticLines.push("");
746
- const brandResult = resolveThemeBrand(input.config.brand, { scope: scopeClass });
804
+ const brandResult = resolveThemeBrand(input.config.brand, { scope: scopeClass, colorScheme });
747
805
  const passthroughCss = generateBrandPassthroughCss(
748
806
  collectBrandPassthrough(input.tokens, input.config.overrides),
749
807
  {
750
- light: `html:not(.dark) ${scopeClass}`,
751
- dark: `.dark ${scopeClass}`,
808
+ light: lightSel,
809
+ dark: darkSel,
752
810
  prefers: `${scopeClass}:not(.light):not(.theme-light):not([data-theme="light"])`
753
- }
811
+ },
812
+ colorScheme
754
813
  );
755
814
  const adaptiveLayer = wrapInLayer("visor-adaptive", lines.join("\n").trim());
756
815
  const semanticLayer = wrapInLayer("visor-semantic", semanticLines.join("\n").trim());
@@ -829,7 +829,7 @@ function block(selector, decls) {
829
829
  if (decls.length === 0) return "";
830
830
  return [`${selector} {`, ...decls.map((d) => ` ${d}`), "}"].join("\n");
831
831
  }
832
- function generateBrandCSS(resolutions, scope) {
832
+ function generateBrandCSS(resolutions, scope, colorScheme = "adaptive") {
833
833
  if (resolutions.length === 0) return "";
834
834
  const baseSelector = scope ? scope : ":root";
835
835
  const lightSelector = scope ? `html:not(.dark) ${scope}` : ":root";
@@ -840,6 +840,18 @@ function generateBrandCSS(resolutions, scope) {
840
840
  lines.push("/* --- Brand: forced-mode aliases + tokens --- */");
841
841
  lines.push(block(baseSelector, staticDecls));
842
842
  lines.push("");
843
+ if (colorScheme === "dark-only") {
844
+ const darkDecls2 = resolutions.map((r) => modeDecl(r, "dark"));
845
+ lines.push("/* --- Brand: variants (dark) \u2014 host --- */");
846
+ lines.push(block(baseSelector, darkDecls2));
847
+ return lines.join("\n").trim();
848
+ }
849
+ if (colorScheme === "light-only") {
850
+ const lightDecls2 = resolutions.map((r) => modeDecl(r, "light"));
851
+ lines.push("/* --- Brand: variants (light) \u2014 host --- */");
852
+ lines.push(block(baseSelector, lightDecls2));
853
+ return lines.join("\n").trim();
854
+ }
843
855
  const lightDecls = resolutions.map((r) => modeDecl(r, "light"));
844
856
  lines.push("/* --- Brand: variants (light) --- */");
845
857
  lines.push(block(lightSelector, lightDecls));
@@ -860,6 +872,7 @@ ${inner.split("\n").map((l) => ` ${l}`).join("\n")}
860
872
  function resolveThemeBrand(brand, options) {
861
873
  const effective = brand ?? DEFAULT_VISOR_BRAND;
862
874
  const scope = options?.scope ?? "";
875
+ const colorScheme = options?.colorScheme ?? "adaptive";
863
876
  const source = resolveBrandSource(effective);
864
877
  const org = effective.org ?? null;
865
878
  const cdnBase = effective["cdn-overrides"]?.["visor-brands"] ?? null;
@@ -881,7 +894,7 @@ function resolveThemeBrand(brand, options) {
881
894
  if (resolution.guidance) warnings.push(resolution.guidance);
882
895
  }
883
896
  }
884
- const css = generateBrandCSS([...variants, ...custom], scope);
897
+ const css = generateBrandCSS([...variants, ...custom], scope, colorScheme);
885
898
  return { variants, custom, css, warnings };
886
899
  }
887
900
 
@@ -1730,9 +1743,15 @@ function buildAdaptiveDecls(tokens, theme) {
1730
1743
  return { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls };
1731
1744
  }
1732
1745
  function generateLightCss(tokens, options) {
1746
+ const colorScheme = options?.colorScheme ?? "adaptive";
1747
+ if (colorScheme === "dark-only") return "";
1733
1748
  const lines = [];
1734
1749
  const { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls } = buildAdaptiveDecls(tokens, "light");
1735
1750
  const host = options?.scopePrefix ?? ":root";
1751
+ if (colorScheme === "light-only") {
1752
+ lines.push(sectionComment("Adaptive: color-scheme (light)"));
1753
+ lines.push(block2(host, ["color-scheme: light;"]));
1754
+ }
1736
1755
  lines.push(sectionComment("Adaptive: Text (light)"));
1737
1756
  lines.push(block2(host, textDecls));
1738
1757
  lines.push(sectionComment("Adaptive: Surface (light)"));
@@ -1748,8 +1767,28 @@ function generateLightCss(tokens, options) {
1748
1767
  return header("Visor Theme \u2014 Light") + lines.join("\n");
1749
1768
  }
1750
1769
  function generateDarkCss(tokens, options) {
1770
+ const colorScheme = options?.colorScheme ?? "adaptive";
1771
+ if (colorScheme === "light-only") return "";
1751
1772
  const lines = [];
1752
1773
  const { textDecls, surfaceDecls, borderDecls, interactiveDecls, intentDecls, hairlineDecls } = buildAdaptiveDecls(tokens, "dark");
1774
+ if (colorScheme === "dark-only") {
1775
+ const host = options?.scopePrefix ?? ":root";
1776
+ lines.push(sectionComment("Adaptive: color-scheme (dark)"));
1777
+ lines.push(block2(host, ["color-scheme: dark;"]));
1778
+ lines.push(sectionComment("Adaptive: Text (dark) \u2014 host"));
1779
+ lines.push(block2(host, textDecls));
1780
+ lines.push(sectionComment("Adaptive: Surface (dark) \u2014 host"));
1781
+ lines.push(block2(host, surfaceDecls));
1782
+ lines.push(sectionComment("Adaptive: Border (dark) \u2014 host"));
1783
+ lines.push(block2(host, borderDecls));
1784
+ lines.push(sectionComment("Adaptive: Interactive (dark) \u2014 host"));
1785
+ lines.push(block2(host, interactiveDecls));
1786
+ lines.push(sectionComment("Adaptive: Intent aliases (dark) \u2014 host"));
1787
+ lines.push(block2(host, intentDecls));
1788
+ lines.push(sectionComment("Adaptive: Hairline aliases (dark) \u2014 host"));
1789
+ lines.push(block2(host, hairlineDecls));
1790
+ return header("Visor Theme \u2014 Dark") + lines.join("\n");
1791
+ }
1753
1792
  const prefix = options?.scopePrefix;
1754
1793
  const darkSelectors = prefix ? [`${prefix}.dark`, `${prefix}.theme-dark`, `${prefix}[data-theme="dark"]`] : [".dark", ".theme-dark", '[data-theme="dark"]'];
1755
1794
  const darkSelector = darkSelectors.join(",\n");
@@ -1829,6 +1868,29 @@ function generateFullBundleCss(primitives, tokens, config) {
1829
1868
  );
1830
1869
  const primitivesBody = generatePrimitivesCss(primitives, config).split("\n").slice(6).join("\n");
1831
1870
  lines.push(primitivesBody);
1871
+ const colorScheme = config["color-scheme"] ?? "adaptive";
1872
+ if (colorScheme === "dark-only") {
1873
+ lines.push(
1874
+ "/* ============================================",
1875
+ " Tier 3: Adaptive \u2014 Dark Theme (:root host) + color-scheme: dark",
1876
+ " ============================================ */"
1877
+ );
1878
+ lines.push(
1879
+ generateDarkCss(tokens, { colorScheme }).split("\n").slice(6).join("\n")
1880
+ );
1881
+ return lines.join("\n");
1882
+ }
1883
+ if (colorScheme === "light-only") {
1884
+ lines.push(
1885
+ "/* ============================================",
1886
+ " Tier 3: Adaptive \u2014 Light Theme (:root host) + color-scheme: light",
1887
+ " ============================================ */"
1888
+ );
1889
+ lines.push(
1890
+ generateLightCss(tokens, { colorScheme }).split("\n").slice(6).join("\n")
1891
+ );
1892
+ return lines.join("\n");
1893
+ }
1832
1894
  lines.push(
1833
1895
  "/* ============================================",
1834
1896
  " Tier 3: Adaptive \u2014 Light Theme (:root)",