@ttoss/fsl-theme 1.1.21 → 2.0.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 +16 -14
  2. package/dist/{Types-BiBa17RL.d.cts → Types-BkaxtsMv.d.cts} +61 -13
  3. package/dist/{Types-BiBa17RL.d.mts → Types-BkaxtsMv.d.mts} +61 -13
  4. package/dist/{baseBundle-BXvPJYzj.cjs → baseBundle-DdRSa5mf.cjs} +3 -2
  5. package/dist/{baseBundle-BLvD8XVS.mjs → baseBundle-R7WyjyTw.mjs} +3 -2
  6. package/dist/{createTheme-JOu16Shb.cjs → createTheme--UFgVlEn.cjs} +144 -16
  7. package/dist/{createTheme-DzLnsknT.mjs → createTheme-B6tVIikC.mjs} +138 -16
  8. package/dist/css.cjs +2 -2
  9. package/dist/css.d.cts +1 -1
  10. package/dist/css.d.mts +1 -1
  11. package/dist/css.mjs +2 -2
  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-C29fy-Ad.cjs → helpers-BU40JvFA.cjs} +27 -78
  21. package/dist/{helpers-CA4Qkxct.mjs → helpers-BXaKe2s3.mjs} +28 -73
  22. package/dist/{index-CsIjfw86.d.cts → index-7YL90Xqs.d.cts} +36 -12
  23. package/dist/{index-nJrjI0BA.d.mts → index-BmCTIOzh.d.mts} +36 -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-DnKxR2gK.d.mts → react-C8JrZf-b.d.mts} +43 -2
  29. package/dist/{react-CoWIQYx_.cjs → react-DLB8nrXQ.cjs} +32 -2
  30. package/dist/{react-CGa6FlNL.d.cts → react-Dt7vGAVG.d.cts} +43 -2
  31. package/dist/react.cjs +1 -1
  32. package/dist/react.d.cts +1 -1
  33. package/dist/react.d.mts +1 -1
  34. package/dist/react.mjs +32 -2
  35. package/dist/runtime-entry.cjs +1 -1
  36. package/dist/runtime-entry.mjs +1 -1
  37. package/dist/{ssrScript-CoRE6jjq.mjs → ssrScript-DNLlLDps.mjs} +2 -1
  38. package/dist/{ssrScript-DOpLExaG.cjs → ssrScript-MF5SIONH.cjs} +2 -1
  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-CUiqx2kh.cjs → toCssVars-BseqmMcT.cjs} +27 -2
  44. package/dist/{toCssVars-BHRiSykR.mjs → toCssVars-C-9yIZDR.mjs} +27 -2
  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 +3 -3
  48. package/dist/vars.d.cts +1 -1
  49. package/dist/vars.d.mts +1 -1
  50. package/dist/vars.mjs +3 -3
  51. package/dist/{withDataviz-DYV1S_xr.mjs → withDataviz-BvpZJnFx.mjs} +2 -2
  52. package/dist/{withDataviz-BlJcYhcw.cjs → withDataviz-CYRf4exT.cjs} +2 -2
  53. package/llms.txt +6 -6
  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,8 +1,19 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
2
  //#region src/roots/helpers.ts
3
- /** Check if a value is a token reference like `{core.colors.brand.500}` */
3
+ /**
4
+ * Check if a value is a **pure** token reference like `{core.colors.brand.500}`.
5
+ *
6
+ * A pure ref is a single `{path}` with no interior braces. Compound expressions
7
+ * (`clamp({a}, {b})`) and multi-ref strings (`{a} {b}`) are NOT pure refs — they
8
+ * start `{`/end `}` but carry interior braces, and must go through the compound
9
+ * resolution path instead. Without the interior-brace check `{a} {b}` would be
10
+ * mis-parsed as the single path `a} {b`.
11
+ */
12
+ var hasInteriorBraces = value => {
13
+ return value.indexOf("{", 1) !== -1 || value.indexOf("}") !== value.length - 1;
14
+ };
4
15
  var isTokenRef = value => {
5
- return typeof value === "string" && value.length > 2 && value.startsWith("{") && value.endsWith("}");
16
+ return typeof value === "string" && value.length > 2 && value.startsWith("{") && value.endsWith("}") && !hasInteriorBraces(value);
6
17
  };
7
18
  /** Extract the inner path from a token reference: `{core.colors.brand.500}` → `core.colors.brand.500` */
8
19
  var extractRefPath = ref => {
@@ -24,6 +35,14 @@ var isPlainObject = value => {
24
35
  return typeof value === "object" && value !== null && !Array.isArray(value);
25
36
  };
26
37
  /**
38
+ * Prototype-chain keys that must never be written from override data —
39
+ * `result['__proto__']` reaches the prototype setter, not an own property.
40
+ * Guards `createTheme`'s public `overrides` against prototype pollution.
41
+ */
42
+ var isUnsafeMergeKey = key => {
43
+ return key === "__proto__" || key === "constructor" || key === "prototype";
44
+ };
45
+ /**
27
46
  * Recursively merges `overrides` into `base`.
28
47
  * - Plain objects are merged recursively.
29
48
  * - All other values (primitives, arrays) are replaced.
@@ -34,6 +53,7 @@ var deepMerge = (base, overrides) => {
34
53
  ...base
35
54
  };
36
55
  for (const key of Object.keys(overrides)) {
56
+ if (isUnsafeMergeKey(key)) continue;
37
57
  const baseVal = result[key];
38
58
  const overVal = overrides[key];
39
59
  if (overVal === void 0) continue;
@@ -69,73 +89,6 @@ var flattenTheme = theme => {
69
89
  };
70
90
  };
71
91
  /**
72
- * Compute the Levenshtein edit distance between two strings.
73
- * Used exclusively by `validateRefs` to power "did you mean?" suggestions.
74
- */
75
- var levenshtein = (a, b) => {
76
- const m = a.length;
77
- const n = b.length;
78
- const dp = Array.from({
79
- length: m + 1
80
- }, () => {
81
- return Array.from({
82
- length: n + 1
83
- }, () => {
84
- return 0;
85
- });
86
- });
87
- for (let i = 0; i <= m; i++) dp[i][0] = i;
88
- for (let j = 0; j <= n; j++) dp[0][j] = j;
89
- for (let i = 1; i <= m; i++) for (let j = 1; j <= n; j++) dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
90
- return dp[m][n];
91
- };
92
- /**
93
- * Validate that every `{ref}` in the merged theme points to an existing path.
94
- * Emits `console.warn` for each broken reference with a "did you mean?" suggestion.
95
- *
96
- * **DEV-only** — callers gate this behind `process.env.NODE_ENV !== 'production'`
97
- * so bundlers tree-shake the entire call in production builds.
98
- */
99
- var validateRefs = theme => {
100
- const {
101
- core,
102
- semantic
103
- } = flattenTheme(theme);
104
- const all = {
105
- ...core,
106
- ...semantic
107
- };
108
- const allKeys = Object.keys(all);
109
- const findSuggestion = (refPath, candidates) => {
110
- if (candidates.length === 0) return "";
111
- let bestDist = Infinity;
112
- let bestKey = "";
113
- for (const candidate of candidates) {
114
- const dist = levenshtein(refPath, candidate);
115
- if (dist < bestDist) {
116
- bestDist = dist;
117
- bestKey = candidate;
118
- }
119
- }
120
- if (bestDist <= Math.ceil(refPath.length * .4)) return `\n Did you mean '{${bestKey}}'?`;
121
- return "";
122
- };
123
- for (const [ownerKey, value] of Object.entries(all)) {
124
- if (typeof value !== "string" || !value.includes("{")) continue;
125
- let match;
126
- const re = new RegExp(COMPOUND_REF_RE.source, COMPOUND_REF_RE.flags);
127
- while ((match = re.exec(value)) !== null) {
128
- const refPath = match[1];
129
- if (all[refPath] !== void 0) continue;
130
- const prefix = refPath.split(".")[0];
131
- const suggestion = findSuggestion(refPath, allKeys.filter(k => {
132
- return k.startsWith(prefix + ".");
133
- }));
134
- console.warn(`[fsl-theme] Invalid token reference '{${refPath}}' at path '${ownerKey}'.${suggestion}`);
135
- }
136
- }
137
- };
138
- /**
139
92
  * Flatten a `ThemeTokens` into a `Record<string, string | number>` with
140
93
  * every `{ref}` recursively resolved to its final raw value where possible.
141
94
  *
@@ -181,8 +134,8 @@ var toFlatTokens = (theme, options = {}) => {
181
134
  /**
182
135
  * Resolve all embedded `{path}` refs in a raw string expression.
183
136
  *
184
- * Handles both pure refs (`{core.space.4}`) and compound expressions
185
- * (`clamp({core.space.4}, {core.space.6}, {core.space.12})`).
137
+ * Handles both pure refs (`{core.spacing.4}`) and compound expressions
138
+ * (`clamp({core.spacing.4}, {core.spacing.6}, {core.spacing.12})`).
186
139
  */
187
140
  const resolveInline = (value, seen, ownerKey) => {
188
141
  return value.replace(COMPOUND_REF_RE, (_match, path) => {
@@ -195,7 +148,9 @@ var toFlatTokens = (theme, options = {}) => {
195
148
  reportUnresolved(ownerKey, path, "circular reference");
196
149
  return `{${path}}`;
197
150
  }
198
- const resolved = resolveRef(target, new Set(seen).add(path), ownerKey);
151
+ const childSeen = new Set(seen).add(path);
152
+ const resolved = resolveRef(target, childSeen, ownerKey);
153
+ if (typeof resolved === "string" && resolved.includes("{")) return resolveInline(resolved, childSeen, ownerKey);
199
154
  return String(resolved);
200
155
  });
201
156
  };
@@ -208,4 +163,4 @@ var toFlatTokens = (theme, options = {}) => {
208
163
  };
209
164
 
210
165
  //#endregion
211
- export { flattenTheme as a, validateRefs as c, flattenObject as i, SAFE_ID_RE as n, isPlainObject as o, deepMerge as r, toFlatTokens as s, COMPOUND_REF_RE as t };
166
+ export { flattenTheme as a, flattenObject as i, SAFE_ID_RE as n, isPlainObject as o, deepMerge as r, toFlatTokens as s, COMPOUND_REF_RE as t };
@@ -1,11 +1,38 @@
1
1
 
2
- import { a as DeepPartial, i as ThemeTokens, r as ThemeBundle, t as ModeOverride } from "./Types-BiBa17RL.cjs";
2
+ import { a as ThemeTokens, i as ThemeBundle, o as DeepPartial, r as ThemeBrief, t as ModeOverride } from "./Types-BkaxtsMv.cjs";
3
3
  //#region src/baseTheme.d.ts
4
4
  /**
5
5
  * **Foundation** — Neutral baseline theme.
6
6
  *
7
7
  * System fonts, gray palette, and balanced proportions. Serves as the
8
8
  * canonical base that all other themes extend via `createTheme`.
9
+ *
10
+ * ## Theme brief
11
+ *
12
+ * Every value in this file is chosen so that the resolved bundle satisfies
13
+ * this brief. Changes that contradict the brief belong in a different theme,
14
+ * not in `baseTheme`.
15
+ *
16
+ * ```yaml
17
+ * name: base
18
+ * purpose: default built-in foundation for modern product UI
19
+ * primaryPosture: productive
20
+ * secondaryPosture: calm
21
+ * densityProfile: balanced
22
+ * readingMode: mixed
23
+ * pointerProfile: hybrid
24
+ * interactionRisk: medium
25
+ * surfaceModel: lightly-layered
26
+ * brandEnergy: quiet
27
+ * accessibilityTarget: AA+
28
+ * colorModeStrategy: dark-supported
29
+ * platformBias: web
30
+ * ```
31
+ *
32
+ * Intended feel: practical, calm, modern, trustworthy, easy to extend.
33
+ * Avoided feel: flashy, ornamental, cramped, fragile, overly branded.
34
+ *
35
+ * @see /docs/website/docs/design/design-system/design-tokens/theme-authoring.md#recommended-base-theme-brief
9
36
  */
10
37
  declare const baseTheme: ThemeTokens;
11
38
  declare const darkAlternate: ModeOverride;
@@ -58,7 +85,8 @@ declare const createTheme: ({
58
85
  baseMode,
59
86
  base,
60
87
  overrides,
61
- alternate
88
+ alternate,
89
+ brief
62
90
  }?: {
63
91
  /**
64
92
  * Parent bundle to inherit from. `base`, `baseMode`, and `alternate` all
@@ -77,18 +105,14 @@ declare const createTheme: ({
77
105
  * Pass `null` to opt out of any alternate (single-mode theme).
78
106
  */
79
107
  alternate?: ModeOverride | null;
108
+ /**
109
+ * Machine-readable design brief. Inherited from `extends.meta` when omitted.
110
+ * Orthogonal to tokens — never affects DTCG/CSS output.
111
+ */
112
+ brief?: ThemeBrief;
80
113
  }) => ThemeBundle;
81
114
  //#endregion
82
115
  //#region src/themes/bruttal.d.ts
83
116
  declare const bruttal: ThemeBundle;
84
117
  //#endregion
85
- //#region src/themes/corporate.d.ts
86
- declare const corporate: ThemeBundle;
87
- //#endregion
88
- //#region src/themes/oca.d.ts
89
- declare const oca: ThemeBundle;
90
- //#endregion
91
- //#region src/themes/ventures.d.ts
92
- declare const ventures: ThemeBundle;
93
- //#endregion
94
- export { createTheme as a, bruttal as i, oca as n, baseTheme as o, corporate as r, darkAlternate as s, ventures as t };
118
+ export { darkAlternate as i, createTheme as n, baseTheme as r, bruttal as t };
@@ -1,11 +1,38 @@
1
1
 
2
- import { a as DeepPartial, i as ThemeTokens, r as ThemeBundle, t as ModeOverride } from "./Types-BiBa17RL.mjs";
2
+ import { a as ThemeTokens, i as ThemeBundle, o as DeepPartial, r as ThemeBrief, t as ModeOverride } from "./Types-BkaxtsMv.mjs";
3
3
  //#region src/baseTheme.d.ts
4
4
  /**
5
5
  * **Foundation** — Neutral baseline theme.
6
6
  *
7
7
  * System fonts, gray palette, and balanced proportions. Serves as the
8
8
  * canonical base that all other themes extend via `createTheme`.
9
+ *
10
+ * ## Theme brief
11
+ *
12
+ * Every value in this file is chosen so that the resolved bundle satisfies
13
+ * this brief. Changes that contradict the brief belong in a different theme,
14
+ * not in `baseTheme`.
15
+ *
16
+ * ```yaml
17
+ * name: base
18
+ * purpose: default built-in foundation for modern product UI
19
+ * primaryPosture: productive
20
+ * secondaryPosture: calm
21
+ * densityProfile: balanced
22
+ * readingMode: mixed
23
+ * pointerProfile: hybrid
24
+ * interactionRisk: medium
25
+ * surfaceModel: lightly-layered
26
+ * brandEnergy: quiet
27
+ * accessibilityTarget: AA+
28
+ * colorModeStrategy: dark-supported
29
+ * platformBias: web
30
+ * ```
31
+ *
32
+ * Intended feel: practical, calm, modern, trustworthy, easy to extend.
33
+ * Avoided feel: flashy, ornamental, cramped, fragile, overly branded.
34
+ *
35
+ * @see /docs/website/docs/design/design-system/design-tokens/theme-authoring.md#recommended-base-theme-brief
9
36
  */
10
37
  declare const baseTheme: ThemeTokens;
11
38
  declare const darkAlternate: ModeOverride;
@@ -58,7 +85,8 @@ declare const createTheme: ({
58
85
  baseMode,
59
86
  base,
60
87
  overrides,
61
- alternate
88
+ alternate,
89
+ brief
62
90
  }?: {
63
91
  /**
64
92
  * Parent bundle to inherit from. `base`, `baseMode`, and `alternate` all
@@ -77,18 +105,14 @@ declare const createTheme: ({
77
105
  * Pass `null` to opt out of any alternate (single-mode theme).
78
106
  */
79
107
  alternate?: ModeOverride | null;
108
+ /**
109
+ * Machine-readable design brief. Inherited from `extends.meta` when omitted.
110
+ * Orthogonal to tokens — never affects DTCG/CSS output.
111
+ */
112
+ brief?: ThemeBrief;
80
113
  }) => ThemeBundle;
81
114
  //#endregion
82
115
  //#region src/themes/bruttal.d.ts
83
116
  declare const bruttal: ThemeBundle;
84
117
  //#endregion
85
- //#region src/themes/corporate.d.ts
86
- declare const corporate: ThemeBundle;
87
- //#endregion
88
- //#region src/themes/oca.d.ts
89
- declare const oca: ThemeBundle;
90
- //#endregion
91
- //#region src/themes/ventures.d.ts
92
- declare const ventures: ThemeBundle;
93
- //#endregion
94
- export { createTheme as a, bruttal as i, oca as n, baseTheme as o, corporate as r, darkAlternate as s, ventures as t };
118
+ export { darkAlternate as i, createTheme as n, baseTheme as r, bruttal as t };
package/dist/index.cjs CHANGED
@@ -2,15 +2,9 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_createTheme = require('./createTheme-JOu16Shb.cjs');
5
+ const require_createTheme = require('./createTheme--UFgVlEn.cjs');
6
6
  const require_themes_bruttal = require('./themes/bruttal.cjs');
7
- const require_themes_corporate = require('./themes/corporate.cjs');
8
- const require_themes_oca = require('./themes/oca.cjs');
9
- const require_themes_ventures = require('./themes/ventures.cjs');
10
7
  exports.baseTheme = require_createTheme.baseTheme;
11
8
  exports.bruttal = require_themes_bruttal.bruttal;
12
- exports.corporate = require_themes_corporate.corporate;
13
9
  exports.createTheme = require_createTheme.createTheme;
14
- exports.darkAlternate = require_createTheme.darkAlternate;
15
- exports.oca = require_themes_oca.oca;
16
- exports.ventures = require_themes_ventures.ventures;
10
+ exports.darkAlternate = require_createTheme.darkAlternate;
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
- import { a as DeepPartial, i as ThemeTokens, n as SemanticTokens, r as ThemeBundle, t as ModeOverride } from "./Types-BiBa17RL.cjs";
3
- import { a as createTheme, i as bruttal, n as oca, o as baseTheme, r as corporate, s as darkAlternate, t as ventures } from "./index-CsIjfw86.cjs";
2
+ import { a as ThemeTokens, i as ThemeBundle, n as SemanticTokens, o as DeepPartial, r as ThemeBrief, t as ModeOverride } from "./Types-BkaxtsMv.cjs";
3
+ import { i as darkAlternate, n as createTheme, r as baseTheme, t as bruttal } from "./index-7YL90Xqs.cjs";
4
4
  import { o as ThemeMode } from "./ssrScript-BVysxDws.cjs";
5
- import { c as ThemeStylesProps, n as ThemeHeadProps } from "./react-CGa6FlNL.cjs";
6
- export { type DeepPartial, type ModeOverride, type SemanticTokens, type ThemeBundle, type ThemeHeadProps, type ThemeMode, type ThemeStylesProps, type ThemeTokens, baseTheme, bruttal, corporate, createTheme, darkAlternate, oca, ventures };
5
+ import { c as ThemeStylesProps, n as ThemeHeadProps } from "./react-Dt7vGAVG.cjs";
6
+ export { type DeepPartial, type ModeOverride, type SemanticTokens, type ThemeBrief, type ThemeBundle, type ThemeHeadProps, type ThemeMode, type ThemeStylesProps, type ThemeTokens, baseTheme, bruttal, createTheme, darkAlternate };
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
 
2
- import { a as DeepPartial, i as ThemeTokens, n as SemanticTokens, r as ThemeBundle, t as ModeOverride } from "./Types-BiBa17RL.mjs";
3
- import { a as createTheme, i as bruttal, n as oca, o as baseTheme, r as corporate, s as darkAlternate, t as ventures } from "./index-nJrjI0BA.mjs";
2
+ import { a as ThemeTokens, i as ThemeBundle, n as SemanticTokens, o as DeepPartial, r as ThemeBrief, t as ModeOverride } from "./Types-BkaxtsMv.mjs";
3
+ import { i as darkAlternate, n as createTheme, r as baseTheme, t as bruttal } from "./index-BmCTIOzh.mjs";
4
4
  import { o as ThemeMode } from "./ssrScript-BVysxDws.mjs";
5
- import { c as ThemeStylesProps, n as ThemeHeadProps } from "./react-DnKxR2gK.mjs";
6
- export { type DeepPartial, type ModeOverride, type SemanticTokens, type ThemeBundle, type ThemeHeadProps, type ThemeMode, type ThemeStylesProps, type ThemeTokens, baseTheme, bruttal, corporate, createTheme, darkAlternate, oca, ventures };
5
+ import { c as ThemeStylesProps, n as ThemeHeadProps } from "./react-C8JrZf-b.mjs";
6
+ export { type DeepPartial, type ModeOverride, type SemanticTokens, type ThemeBrief, type ThemeBundle, type ThemeHeadProps, type ThemeMode, type ThemeStylesProps, type ThemeTokens, baseTheme, bruttal, createTheme, darkAlternate };
package/dist/index.mjs CHANGED
@@ -1,7 +1,4 @@
1
1
  /** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
2
- import { i as darkAlternate, n as createTheme, r as baseTheme } from "./createTheme-DzLnsknT.mjs";
2
+ import { a as darkAlternate, i as baseTheme, n as createTheme } from "./createTheme-B6tVIikC.mjs";
3
3
  import { bruttal } from "./themes/bruttal.mjs";
4
- import { corporate } from "./themes/corporate.mjs";
5
- import { oca } from "./themes/oca.mjs";
6
- import { ventures } from "./themes/ventures.mjs";
7
- export { baseTheme, bruttal, corporate, createTheme, darkAlternate, oca, ventures };
4
+ export { baseTheme, bruttal, createTheme, darkAlternate };
@@ -1,5 +1,5 @@
1
1
 
2
- import { n as SemanticTokens, r as ThemeBundle } from "./Types-BiBa17RL.mjs";
2
+ import { i as ThemeBundle, n as SemanticTokens } from "./Types-BkaxtsMv.mjs";
3
3
  import { a as ResolvedMode, o as ThemeMode, t as ThemeScriptConfig } from "./ssrScript-BVysxDws.mjs";
4
4
  import * as React from "react";
5
5
 
@@ -24,7 +24,15 @@ interface ThemeProviderProps {
24
24
  *
25
25
  * Passing `theme` enables `useTokens()` and `useResolvedTokens()` for all
26
26
  * descendants, and automatically injects the CSS Custom Properties `<style>`
27
- * tag into the document head (React 19 hoisting).
27
+ * tag into the document head (React 19 hoisting, deduped by `href`).
28
+ *
29
+ * **React version:** auto-injection into `<head>` requires **React 19** style
30
+ * hoisting. On React 18 the `<style>` renders inline where the provider sits
31
+ * (not hoisted); use `<ThemeHead>` / `<ThemeStyles>` in your `<head>` for
32
+ * explicit injection there — but do not also pass `theme` here, since the
33
+ * href-less `<ThemeHead>`/`<ThemeStyles>` tag does not dedup against this
34
+ * provider's href-keyed one. The stable `href` only collapses multiple themed
35
+ * `<ThemeProvider>`s sharing a `themeId` into one tag on 19.
28
36
  *
29
37
  * @example
30
38
  * ```tsx
@@ -47,6 +55,21 @@ interface ThemeProviderProps {
47
55
  * `resolvedMode` (actual: `'light' | 'dark'`), covering all integration
48
56
  * needs in one callback.
49
57
  *
58
+ * **System mode:** when `mode` is `'system'`, this callback fires whenever
59
+ * the OS `prefers-color-scheme` changes (e.g. automatic dark mode at sunset)
60
+ * — without any user interaction. Avoid side-effects that should only happen
61
+ * on explicit user action (e.g. API calls, toast notifications):
62
+ *
63
+ * ```tsx
64
+ * onModeChange={(mode, resolvedMode) => {
65
+ * // ✓ safe for system-triggered changes
66
+ * analytics.track('modeChanged', { mode, resolvedMode });
67
+ *
68
+ * // ✗ guard explicit user actions
69
+ * if (mode !== 'system') showToast('Theme updated');
70
+ * }}
71
+ * ```
72
+ *
50
73
  * @example
51
74
  * ```tsx
52
75
  * <ThemeProvider
@@ -286,6 +309,10 @@ interface ThemeStylesProps {
286
309
  * `dangerouslySetInnerHTML` is safe: content comes exclusively from
287
310
  * `toCssVars()` (a pure internal function) — no user input is interpolated.
288
311
  *
312
+ * Renders a plain inline `<style>` where you place it (put it in `<head>`).
313
+ * Do **not** also pass `theme` to `<ThemeProvider>` — that injects a second
314
+ * copy; pass `theme` to the head component only (see README SSR section).
315
+ *
289
316
  * @example
290
317
  * ```tsx
291
318
  * // SSR escape hatch — no themeId needed for canonical 1-theme model
@@ -337,6 +364,12 @@ interface ThemeHeadProps extends ThemeStylesProps {
337
364
  * Use in SSR frameworks (Next.js, Remix) where you need explicit `<head>`
338
365
  * injection. In CSR apps, `<ThemeProvider theme={...}>` handles everything.
339
366
  *
367
+ * **`storageKey` invariant:** `ThemeHead` forwards `storageKey` to `ThemeScript`,
368
+ * which reads localStorage before the first paint. `ThemeProvider` reads the
369
+ * same key at runtime. If you pass a custom `storageKey`, pass the **same value**
370
+ * to both `<ThemeHead>` and `<ThemeProvider>` — a mismatch causes a theme flash
371
+ * exactly in the scenario this component was designed to prevent.
372
+ *
340
373
  * @example
341
374
  * ```tsx
342
375
  * // Next.js app/layout.tsx
@@ -358,6 +391,14 @@ interface ThemeHeadProps extends ThemeStylesProps {
358
391
  * );
359
392
  * }
360
393
  * ```
394
+ *
395
+ * @example
396
+ * ```tsx
397
+ * // Custom storageKey — must be identical on both sides
398
+ * <ThemeHead theme={myTheme} storageKey="my-app-theme" />
399
+ * // ...
400
+ * <ThemeProvider theme={myTheme} storageKey="my-app-theme">{children}</ThemeProvider>
401
+ * ```
361
402
  */
362
403
  declare const ThemeHead: ({
363
404
  theme,
@@ -26,14 +26,25 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  }) : target, mod));
27
27
 
28
28
  //#endregion
29
- const require_helpers = require('./helpers-C29fy-Ad.cjs');
29
+ const require_helpers = require('./helpers-BU40JvFA.cjs');
30
30
  const require_css = require('./css.cjs');
31
- const require_ssrScript = require('./ssrScript-DOpLExaG.cjs');
31
+ const require_ssrScript = require('./ssrScript-MF5SIONH.cjs');
32
32
  let react = require("react");
33
33
  react = __toESM(react);
34
34
  let react_jsx_runtime = require("react/jsx-runtime");
35
35
 
36
36
  //#region src/react.tsx
37
+ /**
38
+ * Stable `href` for the theme's hoistable `<style>` tag. React 19 keys style
39
+ * hoisting **and dedup** on `href` + `precedence`, so re-renders and multiple
40
+ * `<ThemeProvider>`s sharing a `themeId` collapse to a single tag, while
41
+ * distinct `themeId`s coexist (micro-frontends). Note: `ThemeStyles` /
42
+ * `ThemeHead` render an href-less inline `<style>`, so they do **not** dedup
43
+ * against this hoisted tag — don't combine them with a themed `ThemeProvider`.
44
+ */
45
+ var themeStyleHref = themeId => {
46
+ return `tt-theme-${themeId ?? "root"}`;
47
+ };
37
48
  var COARSE_QUERY = "(any-pointer: coarse)";
38
49
  /**
39
50
  * Subscribe to coarse-pointer media query changes.
@@ -188,6 +199,7 @@ var ThemeProvider = ({
188
199
  });
189
200
  if (theme) return /* @__PURE__ */(0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, {
190
201
  children: [/* @__PURE__ */(0, react_jsx_runtime.jsx)("style", {
202
+ href: themeStyleHref(themeId),
191
203
  precedence: "default",
192
204
  children: cssContent
193
205
  }), /* @__PURE__ */(0, react_jsx_runtime.jsx)(ResolvedTokensCtx.Provider, {
@@ -344,6 +356,10 @@ var ThemeScript = ({
344
356
  * `dangerouslySetInnerHTML` is safe: content comes exclusively from
345
357
  * `toCssVars()` (a pure internal function) — no user input is interpolated.
346
358
  *
359
+ * Renders a plain inline `<style>` where you place it (put it in `<head>`).
360
+ * Do **not** also pass `theme` to `<ThemeProvider>` — that injects a second
361
+ * copy; pass `theme` to the head component only (see README SSR section).
362
+ *
347
363
  * @example
348
364
  * ```tsx
349
365
  * // SSR escape hatch — no themeId needed for canonical 1-theme model
@@ -393,6 +409,12 @@ var ThemeStyles = ({
393
409
  * Use in SSR frameworks (Next.js, Remix) where you need explicit `<head>`
394
410
  * injection. In CSR apps, `<ThemeProvider theme={...}>` handles everything.
395
411
  *
412
+ * **`storageKey` invariant:** `ThemeHead` forwards `storageKey` to `ThemeScript`,
413
+ * which reads localStorage before the first paint. `ThemeProvider` reads the
414
+ * same key at runtime. If you pass a custom `storageKey`, pass the **same value**
415
+ * to both `<ThemeHead>` and `<ThemeProvider>` — a mismatch causes a theme flash
416
+ * exactly in the scenario this component was designed to prevent.
417
+ *
396
418
  * @example
397
419
  * ```tsx
398
420
  * // Next.js app/layout.tsx
@@ -414,6 +436,14 @@ var ThemeStyles = ({
414
436
  * );
415
437
  * }
416
438
  * ```
439
+ *
440
+ * @example
441
+ * ```tsx
442
+ * // Custom storageKey — must be identical on both sides
443
+ * <ThemeHead theme={myTheme} storageKey="my-app-theme" />
444
+ * // ...
445
+ * <ThemeProvider theme={myTheme} storageKey="my-app-theme">{children}</ThemeProvider>
446
+ * ```
417
447
  */
418
448
  var ThemeHead = ({
419
449
  theme,
@@ -1,5 +1,5 @@
1
1
 
2
- import { n as SemanticTokens, r as ThemeBundle } from "./Types-BiBa17RL.cjs";
2
+ import { i as ThemeBundle, n as SemanticTokens } from "./Types-BkaxtsMv.cjs";
3
3
  import { a as ResolvedMode, o as ThemeMode, t as ThemeScriptConfig } from "./ssrScript-BVysxDws.cjs";
4
4
  import * as React from "react";
5
5
 
@@ -24,7 +24,15 @@ interface ThemeProviderProps {
24
24
  *
25
25
  * Passing `theme` enables `useTokens()` and `useResolvedTokens()` for all
26
26
  * descendants, and automatically injects the CSS Custom Properties `<style>`
27
- * tag into the document head (React 19 hoisting).
27
+ * tag into the document head (React 19 hoisting, deduped by `href`).
28
+ *
29
+ * **React version:** auto-injection into `<head>` requires **React 19** style
30
+ * hoisting. On React 18 the `<style>` renders inline where the provider sits
31
+ * (not hoisted); use `<ThemeHead>` / `<ThemeStyles>` in your `<head>` for
32
+ * explicit injection there — but do not also pass `theme` here, since the
33
+ * href-less `<ThemeHead>`/`<ThemeStyles>` tag does not dedup against this
34
+ * provider's href-keyed one. The stable `href` only collapses multiple themed
35
+ * `<ThemeProvider>`s sharing a `themeId` into one tag on 19.
28
36
  *
29
37
  * @example
30
38
  * ```tsx
@@ -47,6 +55,21 @@ interface ThemeProviderProps {
47
55
  * `resolvedMode` (actual: `'light' | 'dark'`), covering all integration
48
56
  * needs in one callback.
49
57
  *
58
+ * **System mode:** when `mode` is `'system'`, this callback fires whenever
59
+ * the OS `prefers-color-scheme` changes (e.g. automatic dark mode at sunset)
60
+ * — without any user interaction. Avoid side-effects that should only happen
61
+ * on explicit user action (e.g. API calls, toast notifications):
62
+ *
63
+ * ```tsx
64
+ * onModeChange={(mode, resolvedMode) => {
65
+ * // ✓ safe for system-triggered changes
66
+ * analytics.track('modeChanged', { mode, resolvedMode });
67
+ *
68
+ * // ✗ guard explicit user actions
69
+ * if (mode !== 'system') showToast('Theme updated');
70
+ * }}
71
+ * ```
72
+ *
50
73
  * @example
51
74
  * ```tsx
52
75
  * <ThemeProvider
@@ -286,6 +309,10 @@ interface ThemeStylesProps {
286
309
  * `dangerouslySetInnerHTML` is safe: content comes exclusively from
287
310
  * `toCssVars()` (a pure internal function) — no user input is interpolated.
288
311
  *
312
+ * Renders a plain inline `<style>` where you place it (put it in `<head>`).
313
+ * Do **not** also pass `theme` to `<ThemeProvider>` — that injects a second
314
+ * copy; pass `theme` to the head component only (see README SSR section).
315
+ *
289
316
  * @example
290
317
  * ```tsx
291
318
  * // SSR escape hatch — no themeId needed for canonical 1-theme model
@@ -337,6 +364,12 @@ interface ThemeHeadProps extends ThemeStylesProps {
337
364
  * Use in SSR frameworks (Next.js, Remix) where you need explicit `<head>`
338
365
  * injection. In CSR apps, `<ThemeProvider theme={...}>` handles everything.
339
366
  *
367
+ * **`storageKey` invariant:** `ThemeHead` forwards `storageKey` to `ThemeScript`,
368
+ * which reads localStorage before the first paint. `ThemeProvider` reads the
369
+ * same key at runtime. If you pass a custom `storageKey`, pass the **same value**
370
+ * to both `<ThemeHead>` and `<ThemeProvider>` — a mismatch causes a theme flash
371
+ * exactly in the scenario this component was designed to prevent.
372
+ *
340
373
  * @example
341
374
  * ```tsx
342
375
  * // Next.js app/layout.tsx
@@ -358,6 +391,14 @@ interface ThemeHeadProps extends ThemeStylesProps {
358
391
  * );
359
392
  * }
360
393
  * ```
394
+ *
395
+ * @example
396
+ * ```tsx
397
+ * // Custom storageKey — must be identical on both sides
398
+ * <ThemeHead theme={myTheme} storageKey="my-app-theme" />
399
+ * // ...
400
+ * <ThemeProvider theme={myTheme} storageKey="my-app-theme">{children}</ThemeProvider>
401
+ * ```
361
402
  */
362
403
  declare const ThemeHead: ({
363
404
  theme,
package/dist/react.cjs CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, {
3
3
  value: 'Module'
4
4
  });
5
- const require_react = require('./react-CoWIQYx_.cjs');
5
+ const require_react = require('./react-DLB8nrXQ.cjs');
6
6
  exports.ThemeHead = require_react.ThemeHead;
7
7
  exports.ThemeProvider = require_react.ThemeProvider;
8
8
  exports.ThemeScript = require_react.ThemeScript;
package/dist/react.d.cts CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  import { a as ResolvedMode, o as ThemeMode } from "./ssrScript-BVysxDws.cjs";
3
- import { a as ThemeScript, c as ThemeStylesProps, d as useResolvedTokens, f as useTokens, i as ThemeProviderProps, l as UseColorModeResult, n as ThemeHeadProps, o as ThemeScriptProps, r as ThemeProvider, s as ThemeStyles, t as ThemeHead, u as useColorMode } from "./react-CGa6FlNL.cjs";
3
+ import { a as ThemeScript, c as ThemeStylesProps, d as useResolvedTokens, f as useTokens, i as ThemeProviderProps, l as UseColorModeResult, n as ThemeHeadProps, o as ThemeScriptProps, r as ThemeProvider, s as ThemeStyles, t as ThemeHead, u as useColorMode } from "./react-Dt7vGAVG.cjs";
4
4
  export { type ResolvedMode, ThemeHead, ThemeHeadProps, type ThemeMode, ThemeProvider, ThemeProviderProps, ThemeScript, ThemeScriptProps, ThemeStyles, ThemeStylesProps, UseColorModeResult, useColorMode, useResolvedTokens, useTokens };
package/dist/react.d.mts CHANGED
@@ -1,4 +1,4 @@
1
1
 
2
2
  import { a as ResolvedMode, o as ThemeMode } from "./ssrScript-BVysxDws.mjs";
3
- import { a as ThemeScript, c as ThemeStylesProps, d as useResolvedTokens, f as useTokens, i as ThemeProviderProps, l as UseColorModeResult, n as ThemeHeadProps, o as ThemeScriptProps, r as ThemeProvider, s as ThemeStyles, t as ThemeHead, u as useColorMode } from "./react-DnKxR2gK.mjs";
3
+ import { a as ThemeScript, c as ThemeStylesProps, d as useResolvedTokens, f as useTokens, i as ThemeProviderProps, l as UseColorModeResult, n as ThemeHeadProps, o as ThemeScriptProps, r as ThemeProvider, s as ThemeStyles, t as ThemeHead, u as useColorMode } from "./react-C8JrZf-b.mjs";
4
4
  export { type ResolvedMode, ThemeHead, ThemeHeadProps, type ThemeMode, ThemeProvider, ThemeProviderProps, ThemeScript, ThemeScriptProps, ThemeStyles, ThemeStylesProps, UseColorModeResult, useColorMode, useResolvedTokens, useTokens };