@ttoss/fsl-theme 1.1.22 → 2.1.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.
Files changed (66) hide show
  1. package/README.md +54 -41
  2. package/dist/{Types-BiBa17RL.d.cts → Types-a_sfpbha.d.cts} +84 -24
  3. package/dist/{Types-BiBa17RL.d.mts → Types-a_sfpbha.d.mts} +84 -24
  4. package/dist/{baseBundle-DiYlHNG5.mjs → baseBundle-CXdxj_re.mjs} +3 -2
  5. package/dist/{baseBundle-BWbICg4p.cjs → baseBundle-a9LDsqYy.cjs} +3 -2
  6. package/dist/{createTheme-BHGc1IFI.cjs → createTheme-B7H0xSVp.cjs} +128 -19
  7. package/dist/{createTheme-C1r5nJco.mjs → createTheme-Bm9LVQ4w.mjs} +123 -20
  8. package/dist/css.cjs +5 -4
  9. package/dist/css.d.cts +21 -2
  10. package/dist/css.d.mts +21 -2
  11. package/dist/css.mjs +5 -4
  12. package/dist/dataviz/index.cjs +2 -2
  13. package/dist/dataviz/index.d.cts +1 -1
  14. package/dist/dataviz/index.d.mts +1 -1
  15. package/dist/dataviz/index.mjs +1 -1
  16. package/dist/dtcg.cjs +26 -9
  17. package/dist/dtcg.d.cts +17 -4
  18. package/dist/dtcg.d.mts +17 -4
  19. package/dist/dtcg.mjs +26 -9
  20. package/dist/{helpers-CIqeJ8yd.cjs → helpers-BU40JvFA.cjs} +27 -5
  21. package/dist/{helpers-Dz7NfNK6.mjs → helpers-BXaKe2s3.mjs} +27 -5
  22. package/dist/{index-BuszkWMy.d.mts → index-BmZQVd3G.d.cts} +9 -12
  23. package/dist/{index-Djro6G4e.d.cts → index-XZsRrMTF.d.mts} +9 -12
  24. package/dist/index.cjs +2 -8
  25. package/dist/index.d.cts +4 -4
  26. package/dist/index.d.mts +4 -4
  27. package/dist/index.mjs +2 -5
  28. package/dist/{react-D2J482kx.d.mts → react-BwsjWKmo.d.mts} +59 -16
  29. package/dist/{react-BKLEzIN8.cjs → react-DQErMGJz.cjs} +107 -19
  30. package/dist/{react-BXqSYnmk.d.cts → react-Drr-QSuX.d.cts} +59 -16
  31. package/dist/react.cjs +3 -1
  32. package/dist/react.d.cts +1 -1
  33. package/dist/react.d.mts +1 -1
  34. package/dist/react.mjs +109 -19
  35. package/dist/runtime-entry.cjs +1 -1
  36. package/dist/runtime-entry.mjs +1 -1
  37. package/dist/{ssrScript-D3mYscp1.cjs → ssrScript-D73WcrCM.cjs} +52 -4
  38. package/dist/{ssrScript-DfmopH-w.mjs → ssrScript-zDK1dqXr.mjs} +52 -4
  39. package/dist/themes/bruttal.cjs +19 -4
  40. package/dist/themes/bruttal.d.cts +1 -1
  41. package/dist/themes/bruttal.d.mts +1 -1
  42. package/dist/themes/bruttal.mjs +19 -4
  43. package/dist/{toCssVars-CeckLjW1.mjs → toCssVars-CWTlp9St.mjs} +65 -5
  44. package/dist/{toCssVars-DDowz4wU.cjs → toCssVars-D0BR23cP.cjs} +65 -5
  45. package/dist/{tokenRegistry-DolhB5Vf.mjs → tokenRegistry-DviyqDeE.mjs} +19 -28
  46. package/dist/{tokenRegistry-B9zMRjpL.cjs → tokenRegistry-f_Bx_BAN.cjs} +19 -28
  47. package/dist/vars.cjs +8 -8
  48. package/dist/vars.d.cts +6 -6
  49. package/dist/vars.d.mts +6 -6
  50. package/dist/vars.mjs +8 -8
  51. package/dist/{withDataviz-BzZwFV2e.cjs → withDataviz-DrUtw8cc.cjs} +2 -2
  52. package/dist/{withDataviz-CrgXLR5A.mjs → withDataviz-Ln3sEwb7.mjs} +2 -2
  53. package/llms.txt +11 -9
  54. package/package.json +1 -1
  55. package/dist/themes/corporate.cjs +0 -34
  56. package/dist/themes/corporate.d.cts +0 -3
  57. package/dist/themes/corporate.d.mts +0 -3
  58. package/dist/themes/corporate.mjs +0 -31
  59. package/dist/themes/oca.cjs +0 -34
  60. package/dist/themes/oca.d.cts +0 -3
  61. package/dist/themes/oca.d.mts +0 -3
  62. package/dist/themes/oca.mjs +0 -31
  63. package/dist/themes/ventures.cjs +0 -34
  64. package/dist/themes/ventures.d.cts +0 -3
  65. package/dist/themes/ventures.d.mts +0 -3
  66. package/dist/themes/ventures.mjs +0 -31
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- const require_helpers = require('./helpers-CIqeJ8yd.cjs');
2
+ const require_helpers = require('./helpers-BU40JvFA.cjs');
3
3
 
4
4
  //#region src/baseTheme.ts
5
5
  /**
@@ -66,6 +66,7 @@ var baseTheme = {
66
66
  100: "#fee2e2",
67
67
  300: "#fca5a5",
68
68
  500: "#ef4444",
69
+ 600: "#dc2626",
69
70
  700: "#b91c1c",
70
71
  900: "#7f1d1d"
71
72
  },
@@ -391,7 +392,7 @@ var baseTheme = {
391
392
  },
392
393
  negative: {
393
394
  background: {
394
- default: "{core.colors.red.500}",
395
+ default: "{core.colors.red.600}",
395
396
  hover: "{core.colors.red.700}",
396
397
  active: "{core.colors.red.900}",
397
398
  disabled: "{core.colors.neutral.200}",
@@ -400,7 +401,7 @@ var baseTheme = {
400
401
  expanded: "{core.colors.red.700}"
401
402
  },
402
403
  border: {
403
- default: "{core.colors.red.500}",
404
+ default: "{core.colors.red.600}",
404
405
  hover: "{core.colors.red.700}",
405
406
  active: "{core.colors.red.900}",
406
407
  focused: "{core.colors.red.700}",
@@ -455,7 +456,8 @@ var baseTheme = {
455
456
  checked: "{core.colors.brand.500}",
456
457
  indeterminate: "{core.colors.brand.300}",
457
458
  pressed: "{core.colors.neutral.100}",
458
- expanded: "{core.colors.neutral.50}"
459
+ expanded: "{core.colors.neutral.50}",
460
+ invalid: "{core.colors.neutral.0}"
459
461
  },
460
462
  border: {
461
463
  default: "{core.colors.neutral.300}",
@@ -467,12 +469,14 @@ var baseTheme = {
467
469
  checked: "{core.colors.brand.500}",
468
470
  indeterminate: "{core.colors.brand.300}",
469
471
  pressed: "{core.colors.neutral.500}",
470
- expanded: "{core.colors.brand.500}"
472
+ expanded: "{core.colors.brand.500}",
473
+ invalid: "{core.colors.red.600}"
471
474
  },
472
475
  text: {
473
476
  default: "{core.colors.neutral.900}",
474
477
  disabled: "{core.colors.neutral.500}",
475
478
  checked: "{core.colors.neutral.0}",
479
+ invalid: "{core.colors.neutral.900}",
476
480
  indeterminate: "{core.colors.neutral.900}"
477
481
  }
478
482
  },
@@ -1355,17 +1359,20 @@ var darkAlternate = {
1355
1359
  selected: "{core.colors.brand.700}",
1356
1360
  droptarget: "{core.colors.neutral.700}",
1357
1361
  pressed: "{core.colors.neutral.500}",
1358
- expanded: "{core.colors.neutral.500}"
1362
+ expanded: "{core.colors.neutral.500}",
1363
+ invalid: "{core.colors.neutral.700}"
1359
1364
  },
1360
1365
  border: {
1361
1366
  default: "{core.colors.neutral.500}",
1362
1367
  hover: "{core.colors.neutral.300}",
1363
1368
  focused: "{core.colors.brand.500}",
1364
- disabled: "{core.colors.neutral.700}"
1369
+ disabled: "{core.colors.neutral.700}",
1370
+ invalid: "{core.colors.red.300}"
1365
1371
  },
1366
1372
  text: {
1367
1373
  default: "{core.colors.neutral.0}",
1368
- disabled: "{core.colors.neutral.500}"
1374
+ disabled: "{core.colors.neutral.500}",
1375
+ invalid: "{core.colors.neutral.0}"
1369
1376
  }
1370
1377
  },
1371
1378
  secondary: {
@@ -1741,6 +1748,26 @@ var darkAlternate = {
1741
1748
  }
1742
1749
  }
1743
1750
  };
1751
+ /**
1752
+ * Design brief for the base theme — the recommended default from
1753
+ * theme-authoring.md §"Recommended base theme brief". Attached to the base
1754
+ * bundle via `createTheme({ brief })`; carries the `FSL-DESIGN-001..003` gate.
1755
+ */
1756
+ var baseBrief = {
1757
+ name: "base",
1758
+ purpose: "default built-in foundation for modern product UI",
1759
+ primaryPosture: "productive",
1760
+ secondaryPosture: "calm",
1761
+ densityProfile: "balanced",
1762
+ readingMode: "mixed",
1763
+ pointerProfile: "hybrid",
1764
+ interactionRisk: "medium",
1765
+ surfaceModel: "lightly-layered",
1766
+ brandEnergy: "quiet",
1767
+ accessibilityTarget: "AA+",
1768
+ colorModeStrategy: "dark-supported",
1769
+ platformBias: "web"
1770
+ };
1744
1771
 
1745
1772
  //#endregion
1746
1773
  //#region src/roots/validateRefs.ts
@@ -1769,10 +1796,17 @@ var levenshtein = (a, b) => {
1769
1796
  * Validate that every `{ref}` in the merged theme points to an existing path.
1770
1797
  * Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
1771
1798
  *
1799
+ * `onlyOwnerKeys` restricts which token paths are *checked* (resolution still
1800
+ * looks at the whole tree) — used to validate just the leaves an alternate
1801
+ * introduces without re-warning refs already reported for the base.
1802
+ *
1772
1803
  * **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
1773
1804
  * so bundlers tree-shake the entire call in production builds.
1774
1805
  */
1775
- var validateRefs = theme => {
1806
+ var validateRefs = (theme, options = {}) => {
1807
+ const {
1808
+ onlyOwnerKeys
1809
+ } = options;
1776
1810
  const {
1777
1811
  core,
1778
1812
  semantic
@@ -1797,6 +1831,7 @@ var validateRefs = theme => {
1797
1831
  return "";
1798
1832
  };
1799
1833
  for (const [ownerKey, value] of Object.entries(all)) {
1834
+ if (onlyOwnerKeys && !onlyOwnerKeys.has(ownerKey)) continue;
1800
1835
  if (typeof value !== "string" || !value.includes("{")) continue;
1801
1836
  let match;
1802
1837
  const re = new RegExp(require_helpers.COMPOUND_REF_RE.source, require_helpers.COMPOUND_REF_RE.flags);
@@ -1815,6 +1850,41 @@ var validateRefs = theme => {
1815
1850
  //#endregion
1816
1851
  //#region src/createTheme.ts
1817
1852
  /**
1853
+ * Deep-clone to break shared references between base and result.
1854
+ * Tokens are primitives (strings, numbers) — `structuredClone` is safe and
1855
+ * more correct than a JSON round-trip (preserves `undefined`, `NaN`, etc.).
1856
+ *
1857
+ * Falls back to a JSON round-trip when `structuredClone` is unavailable
1858
+ * (e.g. Jest's jsdom environment, older embedded runtimes) so importing the
1859
+ * package never throws at module-evaluation time. Token trees are plain
1860
+ * string/number leaves, so the fallback is lossless for valid themes.
1861
+ */
1862
+ var cloneTokens = value => {
1863
+ if (typeof structuredClone === "function") return structuredClone(value);
1864
+ return JSON.parse(JSON.stringify(value));
1865
+ };
1866
+ /**
1867
+ * DEV-only: validate the refs an alternate introduces against the built base.
1868
+ * The alternate is authored by hand (semantic-only remaps), so a typo'd
1869
+ * dark-mode ref must warn at theme creation instead of silently emitting a
1870
+ * broken CSS var in production.
1871
+ *
1872
+ * Only the leaves the alternate itself provides are checked — base refs were
1873
+ * already validated by `buildTheme`, so they would otherwise warn twice.
1874
+ */
1875
+ var validateAlternateRefs = ({
1876
+ base,
1877
+ alternate
1878
+ }) => {
1879
+ const alternateKeys = new Set(Object.keys(require_helpers.flattenObject(alternate.semantic, "semantic")));
1880
+ validateRefs({
1881
+ core: base.core,
1882
+ semantic: require_helpers.deepMerge(base.semantic, alternate.semantic)
1883
+ }, {
1884
+ onlyOwnerKeys: alternateKeys
1885
+ });
1886
+ };
1887
+ /**
1818
1888
  * Creates a fully resolved `ThemeTokens` by merging partial overrides into a base.
1819
1889
  * Internal engine — consumers should use `createTheme` which returns `ThemeBundle`.
1820
1890
  *
@@ -1826,13 +1896,28 @@ var buildTheme = ({
1826
1896
  } = {}) => {
1827
1897
  const merged = require_helpers.deepMerge(base, overrides);
1828
1898
  if (process.env.NODE_ENV !== "production") validateRefs(merged);
1829
- return structuredClone(merged);
1899
+ return cloneTokens(merged);
1830
1900
  };
1831
1901
  var resolveAlternate = (alternate, parentBundle) => {
1832
1902
  if (alternate === null) return void 0;
1833
1903
  if (alternate !== void 0) return alternate;
1834
1904
  return parentBundle?.alternate ?? darkAlternate;
1835
1905
  };
1906
+ /** Resolve every `createTheme` input against its `extends` parent + defaults. */
1907
+ var resolveBundleInputs = ({
1908
+ parentBundle,
1909
+ baseMode,
1910
+ base,
1911
+ alternate,
1912
+ brief
1913
+ }) => {
1914
+ return {
1915
+ resolvedBase: base ?? parentBundle?.base,
1916
+ resolvedBaseMode: baseMode ?? parentBundle?.baseMode ?? "light",
1917
+ resolvedAlternate: resolveAlternate(alternate, parentBundle),
1918
+ resolvedMeta: brief ?? parentBundle?.meta
1919
+ };
1920
+ };
1836
1921
  /**
1837
1922
  * Creates a theme bundle with an optional alternate color mode.
1838
1923
  *
@@ -1880,22 +1965,46 @@ var createTheme = ({
1880
1965
  baseMode,
1881
1966
  base,
1882
1967
  overrides,
1883
- alternate
1968
+ alternate,
1969
+ brief
1884
1970
  } = {}) => {
1885
- const resolvedBase = base ?? parentBundle?.base;
1886
- const resolvedBaseMode = baseMode ?? parentBundle?.baseMode ?? "light";
1887
- const resolvedAlternate = resolveAlternate(alternate, parentBundle);
1971
+ const {
1972
+ resolvedBase,
1973
+ resolvedBaseMode,
1974
+ resolvedAlternate,
1975
+ resolvedMeta
1976
+ } = resolveBundleInputs({
1977
+ parentBundle,
1978
+ baseMode,
1979
+ base,
1980
+ alternate,
1981
+ brief
1982
+ });
1983
+ const builtBase = buildTheme({
1984
+ base: resolvedBase,
1985
+ overrides
1986
+ });
1987
+ if (process.env.NODE_ENV !== "production" && resolvedAlternate) validateAlternateRefs({
1988
+ base: builtBase,
1989
+ alternate: resolvedAlternate
1990
+ });
1888
1991
  return {
1889
1992
  baseMode: resolvedBaseMode,
1890
- base: buildTheme({
1891
- base: resolvedBase,
1892
- overrides
1893
- }),
1894
- alternate: resolvedAlternate
1993
+ base: builtBase,
1994
+ alternate: resolvedAlternate,
1995
+ ...(resolvedMeta ? {
1996
+ meta: resolvedMeta
1997
+ } : {})
1895
1998
  };
1896
1999
  };
1897
2000
 
1898
2001
  //#endregion
2002
+ Object.defineProperty(exports, 'baseBrief', {
2003
+ enumerable: true,
2004
+ get: function () {
2005
+ return baseBrief;
2006
+ }
2007
+ });
1899
2008
  Object.defineProperty(exports, 'baseTheme', {
1900
2009
  enumerable: true,
1901
2010
  get: function () {
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { a as flattenTheme, r as deepMerge, t as COMPOUND_REF_RE } from "./helpers-Dz7NfNK6.mjs";
2
+ import { a as flattenTheme, i as flattenObject, r as deepMerge, t as COMPOUND_REF_RE } from "./helpers-BXaKe2s3.mjs";
3
3
 
4
4
  //#region src/baseTheme.ts
5
5
  /**
@@ -66,6 +66,7 @@ var baseTheme = {
66
66
  100: "#fee2e2",
67
67
  300: "#fca5a5",
68
68
  500: "#ef4444",
69
+ 600: "#dc2626",
69
70
  700: "#b91c1c",
70
71
  900: "#7f1d1d"
71
72
  },
@@ -391,7 +392,7 @@ var baseTheme = {
391
392
  },
392
393
  negative: {
393
394
  background: {
394
- default: "{core.colors.red.500}",
395
+ default: "{core.colors.red.600}",
395
396
  hover: "{core.colors.red.700}",
396
397
  active: "{core.colors.red.900}",
397
398
  disabled: "{core.colors.neutral.200}",
@@ -400,7 +401,7 @@ var baseTheme = {
400
401
  expanded: "{core.colors.red.700}"
401
402
  },
402
403
  border: {
403
- default: "{core.colors.red.500}",
404
+ default: "{core.colors.red.600}",
404
405
  hover: "{core.colors.red.700}",
405
406
  active: "{core.colors.red.900}",
406
407
  focused: "{core.colors.red.700}",
@@ -455,7 +456,8 @@ var baseTheme = {
455
456
  checked: "{core.colors.brand.500}",
456
457
  indeterminate: "{core.colors.brand.300}",
457
458
  pressed: "{core.colors.neutral.100}",
458
- expanded: "{core.colors.neutral.50}"
459
+ expanded: "{core.colors.neutral.50}",
460
+ invalid: "{core.colors.neutral.0}"
459
461
  },
460
462
  border: {
461
463
  default: "{core.colors.neutral.300}",
@@ -467,12 +469,14 @@ var baseTheme = {
467
469
  checked: "{core.colors.brand.500}",
468
470
  indeterminate: "{core.colors.brand.300}",
469
471
  pressed: "{core.colors.neutral.500}",
470
- expanded: "{core.colors.brand.500}"
472
+ expanded: "{core.colors.brand.500}",
473
+ invalid: "{core.colors.red.600}"
471
474
  },
472
475
  text: {
473
476
  default: "{core.colors.neutral.900}",
474
477
  disabled: "{core.colors.neutral.500}",
475
478
  checked: "{core.colors.neutral.0}",
479
+ invalid: "{core.colors.neutral.900}",
476
480
  indeterminate: "{core.colors.neutral.900}"
477
481
  }
478
482
  },
@@ -1355,17 +1359,20 @@ var darkAlternate = {
1355
1359
  selected: "{core.colors.brand.700}",
1356
1360
  droptarget: "{core.colors.neutral.700}",
1357
1361
  pressed: "{core.colors.neutral.500}",
1358
- expanded: "{core.colors.neutral.500}"
1362
+ expanded: "{core.colors.neutral.500}",
1363
+ invalid: "{core.colors.neutral.700}"
1359
1364
  },
1360
1365
  border: {
1361
1366
  default: "{core.colors.neutral.500}",
1362
1367
  hover: "{core.colors.neutral.300}",
1363
1368
  focused: "{core.colors.brand.500}",
1364
- disabled: "{core.colors.neutral.700}"
1369
+ disabled: "{core.colors.neutral.700}",
1370
+ invalid: "{core.colors.red.300}"
1365
1371
  },
1366
1372
  text: {
1367
1373
  default: "{core.colors.neutral.0}",
1368
- disabled: "{core.colors.neutral.500}"
1374
+ disabled: "{core.colors.neutral.500}",
1375
+ invalid: "{core.colors.neutral.0}"
1369
1376
  }
1370
1377
  },
1371
1378
  secondary: {
@@ -1741,6 +1748,26 @@ var darkAlternate = {
1741
1748
  }
1742
1749
  }
1743
1750
  };
1751
+ /**
1752
+ * Design brief for the base theme — the recommended default from
1753
+ * theme-authoring.md §"Recommended base theme brief". Attached to the base
1754
+ * bundle via `createTheme({ brief })`; carries the `FSL-DESIGN-001..003` gate.
1755
+ */
1756
+ var baseBrief = {
1757
+ name: "base",
1758
+ purpose: "default built-in foundation for modern product UI",
1759
+ primaryPosture: "productive",
1760
+ secondaryPosture: "calm",
1761
+ densityProfile: "balanced",
1762
+ readingMode: "mixed",
1763
+ pointerProfile: "hybrid",
1764
+ interactionRisk: "medium",
1765
+ surfaceModel: "lightly-layered",
1766
+ brandEnergy: "quiet",
1767
+ accessibilityTarget: "AA+",
1768
+ colorModeStrategy: "dark-supported",
1769
+ platformBias: "web"
1770
+ };
1744
1771
 
1745
1772
  //#endregion
1746
1773
  //#region src/roots/validateRefs.ts
@@ -1769,10 +1796,17 @@ var levenshtein = (a, b) => {
1769
1796
  * Validate that every `{ref}` in the merged theme points to an existing path.
1770
1797
  * Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
1771
1798
  *
1799
+ * `onlyOwnerKeys` restricts which token paths are *checked* (resolution still
1800
+ * looks at the whole tree) — used to validate just the leaves an alternate
1801
+ * introduces without re-warning refs already reported for the base.
1802
+ *
1772
1803
  * **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
1773
1804
  * so bundlers tree-shake the entire call in production builds.
1774
1805
  */
1775
- var validateRefs = theme => {
1806
+ var validateRefs = (theme, options = {}) => {
1807
+ const {
1808
+ onlyOwnerKeys
1809
+ } = options;
1776
1810
  const {
1777
1811
  core,
1778
1812
  semantic
@@ -1797,6 +1831,7 @@ var validateRefs = theme => {
1797
1831
  return "";
1798
1832
  };
1799
1833
  for (const [ownerKey, value] of Object.entries(all)) {
1834
+ if (onlyOwnerKeys && !onlyOwnerKeys.has(ownerKey)) continue;
1800
1835
  if (typeof value !== "string" || !value.includes("{")) continue;
1801
1836
  let match;
1802
1837
  const re = new RegExp(COMPOUND_REF_RE.source, COMPOUND_REF_RE.flags);
@@ -1815,6 +1850,41 @@ var validateRefs = theme => {
1815
1850
  //#endregion
1816
1851
  //#region src/createTheme.ts
1817
1852
  /**
1853
+ * Deep-clone to break shared references between base and result.
1854
+ * Tokens are primitives (strings, numbers) — `structuredClone` is safe and
1855
+ * more correct than a JSON round-trip (preserves `undefined`, `NaN`, etc.).
1856
+ *
1857
+ * Falls back to a JSON round-trip when `structuredClone` is unavailable
1858
+ * (e.g. Jest's jsdom environment, older embedded runtimes) so importing the
1859
+ * package never throws at module-evaluation time. Token trees are plain
1860
+ * string/number leaves, so the fallback is lossless for valid themes.
1861
+ */
1862
+ var cloneTokens = value => {
1863
+ if (typeof structuredClone === "function") return structuredClone(value);
1864
+ return JSON.parse(JSON.stringify(value));
1865
+ };
1866
+ /**
1867
+ * DEV-only: validate the refs an alternate introduces against the built base.
1868
+ * The alternate is authored by hand (semantic-only remaps), so a typo'd
1869
+ * dark-mode ref must warn at theme creation instead of silently emitting a
1870
+ * broken CSS var in production.
1871
+ *
1872
+ * Only the leaves the alternate itself provides are checked — base refs were
1873
+ * already validated by `buildTheme`, so they would otherwise warn twice.
1874
+ */
1875
+ var validateAlternateRefs = ({
1876
+ base,
1877
+ alternate
1878
+ }) => {
1879
+ const alternateKeys = new Set(Object.keys(flattenObject(alternate.semantic, "semantic")));
1880
+ validateRefs({
1881
+ core: base.core,
1882
+ semantic: deepMerge(base.semantic, alternate.semantic)
1883
+ }, {
1884
+ onlyOwnerKeys: alternateKeys
1885
+ });
1886
+ };
1887
+ /**
1818
1888
  * Creates a fully resolved `ThemeTokens` by merging partial overrides into a base.
1819
1889
  * Internal engine — consumers should use `createTheme` which returns `ThemeBundle`.
1820
1890
  *
@@ -1826,13 +1896,28 @@ var buildTheme = ({
1826
1896
  } = {}) => {
1827
1897
  const merged = deepMerge(base, overrides);
1828
1898
  if (process.env.NODE_ENV !== "production") validateRefs(merged);
1829
- return structuredClone(merged);
1899
+ return cloneTokens(merged);
1830
1900
  };
1831
1901
  var resolveAlternate = (alternate, parentBundle) => {
1832
1902
  if (alternate === null) return void 0;
1833
1903
  if (alternate !== void 0) return alternate;
1834
1904
  return parentBundle?.alternate ?? darkAlternate;
1835
1905
  };
1906
+ /** Resolve every `createTheme` input against its `extends` parent + defaults. */
1907
+ var resolveBundleInputs = ({
1908
+ parentBundle,
1909
+ baseMode,
1910
+ base,
1911
+ alternate,
1912
+ brief
1913
+ }) => {
1914
+ return {
1915
+ resolvedBase: base ?? parentBundle?.base,
1916
+ resolvedBaseMode: baseMode ?? parentBundle?.baseMode ?? "light",
1917
+ resolvedAlternate: resolveAlternate(alternate, parentBundle),
1918
+ resolvedMeta: brief ?? parentBundle?.meta
1919
+ };
1920
+ };
1836
1921
  /**
1837
1922
  * Creates a theme bundle with an optional alternate color mode.
1838
1923
  *
@@ -1880,20 +1965,38 @@ var createTheme = ({
1880
1965
  baseMode,
1881
1966
  base,
1882
1967
  overrides,
1883
- alternate
1968
+ alternate,
1969
+ brief
1884
1970
  } = {}) => {
1885
- const resolvedBase = base ?? parentBundle?.base;
1886
- const resolvedBaseMode = baseMode ?? parentBundle?.baseMode ?? "light";
1887
- const resolvedAlternate = resolveAlternate(alternate, parentBundle);
1971
+ const {
1972
+ resolvedBase,
1973
+ resolvedBaseMode,
1974
+ resolvedAlternate,
1975
+ resolvedMeta
1976
+ } = resolveBundleInputs({
1977
+ parentBundle,
1978
+ baseMode,
1979
+ base,
1980
+ alternate,
1981
+ brief
1982
+ });
1983
+ const builtBase = buildTheme({
1984
+ base: resolvedBase,
1985
+ overrides
1986
+ });
1987
+ if (process.env.NODE_ENV !== "production" && resolvedAlternate) validateAlternateRefs({
1988
+ base: builtBase,
1989
+ alternate: resolvedAlternate
1990
+ });
1888
1991
  return {
1889
1992
  baseMode: resolvedBaseMode,
1890
- base: buildTheme({
1891
- base: resolvedBase,
1892
- overrides
1893
- }),
1894
- alternate: resolvedAlternate
1993
+ base: builtBase,
1994
+ alternate: resolvedAlternate,
1995
+ ...(resolvedMeta ? {
1996
+ meta: resolvedMeta
1997
+ } : {})
1895
1998
  };
1896
1999
  };
1897
2000
 
1898
2001
  //#endregion
1899
- export { darkAlternate as i, createTheme as n, baseTheme as r, buildTheme as t };
2002
+ export { darkAlternate as a, baseTheme as i, createTheme as n, baseBrief as r, buildTheme as t };
package/dist/css.cjs CHANGED
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_helpers = require('./helpers-CIqeJ8yd.cjs');
6
- const require_toCssVars = require('./toCssVars-DDowz4wU.cjs');
5
+ const require_helpers = require('./helpers-BU40JvFA.cjs');
6
+ const require_toCssVars = require('./toCssVars-D0BR23cP.cjs');
7
7
 
8
8
  //#region src/css.ts
9
9
  /**
@@ -35,9 +35,10 @@ const require_toCssVars = require('./toCssVars-DDowz4wU.cjs');
35
35
  * res.type('text/css').send(getThemeStylesContent(myBundle, 'myTheme'));
36
36
  * ```
37
37
  */
38
- var getThemeStylesContent = (bundle, themeId) => {
38
+ var getThemeStylesContent = (bundle, themeId, options) => {
39
39
  return require_toCssVars.toCssVars(bundle, {
40
- themeId
40
+ themeId,
41
+ systemModeFallback: options?.systemModeFallback
41
42
  }).toCssString();
42
43
  };
43
44
 
package/dist/css.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ThemeTokens, r as ThemeBundle } from "./Types-BiBa17RL.cjs";
2
+ import { a as ThemeTokens, i as ThemeBundle } from "./Types-a_sfpbha.cjs";
3
3
 
4
4
  //#region src/roots/helpers.d.ts
5
5
  /**
@@ -97,6 +97,18 @@ interface BundleCssVarsOptions {
97
97
  * multi-theme scenarios (Storybook, micro-frontends).
98
98
  */
99
99
  themeId?: string;
100
+ /**
101
+ * Emit the `@media (prefers-color-scheme)` fallback block so the OS
102
+ * preference applies before JS runs (and without JS). Only meaningful for
103
+ * themeId-less bundles. Default `true`.
104
+ *
105
+ * Set `false` when the app is deliberately single-default (`defaultMode`
106
+ * `'light'` or `'dark'` with the alternate reachable only via explicit
107
+ * toggle) — otherwise dark-OS users would get the alternate on first paint
108
+ * against the app's configured default. `<ThemeProvider>` / `<ThemeHead>`
109
+ * derive this automatically from their `defaultMode` prop.
110
+ */
111
+ systemModeFallback?: boolean;
100
112
  }
101
113
  /**
102
114
  * Return type of `bundleToCssVars()`.
@@ -163,6 +175,13 @@ declare function toCssVars(bundle: ThemeBundle, options?: BundleCssVarsOptions):
163
175
  * res.type('text/css').send(getThemeStylesContent(myBundle, 'myTheme'));
164
176
  * ```
165
177
  */
166
- declare const getThemeStylesContent: (bundle: ThemeBundle, themeId?: string) => string;
178
+ declare const getThemeStylesContent: (bundle: ThemeBundle, themeId?: string, options?: {
179
+ /**
180
+ * Emit the `@media (prefers-color-scheme)` fallback block (themeId-less
181
+ * bundles only). Default `true`. Pass `false` for apps whose
182
+ * `defaultMode` is a fixed `'light'`/`'dark'` rather than `'system'`.
183
+ */
184
+ systemModeFallback?: boolean;
185
+ }) => string;
167
186
  //#endregion
168
187
  export { type BundleCssVarsOptions, type BundleCssVarsResult, type CssVarsOptions, type CssVarsResult, type FlatTokenMap, getThemeStylesContent, toCssVarName, toCssVars, toFlatTokens };
package/dist/css.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { i as ThemeTokens, r as ThemeBundle } from "./Types-BiBa17RL.mjs";
2
+ import { a as ThemeTokens, i as ThemeBundle } from "./Types-a_sfpbha.mjs";
3
3
 
4
4
  //#region src/roots/helpers.d.ts
5
5
  /**
@@ -97,6 +97,18 @@ interface BundleCssVarsOptions {
97
97
  * multi-theme scenarios (Storybook, micro-frontends).
98
98
  */
99
99
  themeId?: string;
100
+ /**
101
+ * Emit the `@media (prefers-color-scheme)` fallback block so the OS
102
+ * preference applies before JS runs (and without JS). Only meaningful for
103
+ * themeId-less bundles. Default `true`.
104
+ *
105
+ * Set `false` when the app is deliberately single-default (`defaultMode`
106
+ * `'light'` or `'dark'` with the alternate reachable only via explicit
107
+ * toggle) — otherwise dark-OS users would get the alternate on first paint
108
+ * against the app's configured default. `<ThemeProvider>` / `<ThemeHead>`
109
+ * derive this automatically from their `defaultMode` prop.
110
+ */
111
+ systemModeFallback?: boolean;
100
112
  }
101
113
  /**
102
114
  * Return type of `bundleToCssVars()`.
@@ -163,6 +175,13 @@ declare function toCssVars(bundle: ThemeBundle, options?: BundleCssVarsOptions):
163
175
  * res.type('text/css').send(getThemeStylesContent(myBundle, 'myTheme'));
164
176
  * ```
165
177
  */
166
- declare const getThemeStylesContent: (bundle: ThemeBundle, themeId?: string) => string;
178
+ declare const getThemeStylesContent: (bundle: ThemeBundle, themeId?: string, options?: {
179
+ /**
180
+ * Emit the `@media (prefers-color-scheme)` fallback block (themeId-less
181
+ * bundles only). Default `true`. Pass `false` for apps whose
182
+ * `defaultMode` is a fixed `'light'`/`'dark'` rather than `'system'`.
183
+ */
184
+ systemModeFallback?: boolean;
185
+ }) => string;
167
186
  //#endregion
168
187
  export { type BundleCssVarsOptions, type BundleCssVarsResult, type CssVarsOptions, type CssVarsResult, type FlatTokenMap, getThemeStylesContent, toCssVarName, toCssVars, toFlatTokens };
package/dist/css.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { s as toFlatTokens } from "./helpers-Dz7NfNK6.mjs";
3
- import { n as toCssVars, t as toCssVarName } from "./toCssVars-CeckLjW1.mjs";
2
+ import { s as toFlatTokens } from "./helpers-BXaKe2s3.mjs";
3
+ import { n as toCssVars, t as toCssVarName } from "./toCssVars-CWTlp9St.mjs";
4
4
 
5
5
  //#region src/css.ts
6
6
  /**
@@ -32,9 +32,10 @@ import { n as toCssVars, t as toCssVarName } from "./toCssVars-CeckLjW1.mjs";
32
32
  * res.type('text/css').send(getThemeStylesContent(myBundle, 'myTheme'));
33
33
  * ```
34
34
  */
35
- var getThemeStylesContent = (bundle, themeId) => {
35
+ var getThemeStylesContent = (bundle, themeId, options) => {
36
36
  return toCssVars(bundle, {
37
- themeId
37
+ themeId,
38
+ systemModeFallback: options?.systemModeFallback
38
39
  }).toCssString();
39
40
  };
40
41
 
@@ -2,8 +2,8 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_react = require('../react-BKLEzIN8.cjs');
6
- const require_withDataviz = require('../withDataviz-BzZwFV2e.cjs');
5
+ const require_react = require('../react-DQErMGJz.cjs');
6
+ const require_withDataviz = require('../withDataviz-DrUtw8cc.cjs');
7
7
 
8
8
  //#region src/dataviz/useDatavizTokens.ts
9
9
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { o as CoreDataviz, r as ThemeBundle, s as SemanticDataviz } from "../Types-BiBa17RL.cjs";
2
+ import { c as SemanticDataviz, i as ThemeBundle, s as CoreDataviz } from "../Types-a_sfpbha.cjs";
3
3
 
4
4
  //#region src/dataviz/baseTheme.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
 
2
- import { o as CoreDataviz, r as ThemeBundle, s as SemanticDataviz } from "../Types-BiBa17RL.mjs";
2
+ import { c as SemanticDataviz, i as ThemeBundle, s as CoreDataviz } from "../Types-a_sfpbha.mjs";
3
3
 
4
4
  //#region src/dataviz/baseTheme.d.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { n as coreDataviz, r as semanticDataviz, t as withDataviz } from "../withDataviz-CrgXLR5A.mjs";
2
+ import { n as coreDataviz, r as semanticDataviz, t as withDataviz } from "../withDataviz-Ln3sEwb7.mjs";
3
3
  import { useTokens } from "../react.mjs";
4
4
 
5
5
  //#region src/dataviz/useDatavizTokens.ts