@fluid-app/portal-sdk 0.1.200 → 0.1.201

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 (72) hide show
  1. package/dist/{ContactsScreen-DflUjayA.cjs → ContactsScreen-BQ6pvYOa.cjs} +2 -2
  2. package/dist/{ContactsScreen-DflUjayA.cjs.map → ContactsScreen-BQ6pvYOa.cjs.map} +1 -1
  3. package/dist/{ContactsScreen-BOMxeTG8.cjs → ContactsScreen-Bw2GcYtk.cjs} +2 -2
  4. package/dist/{ContactsScreen-Bm9SlMY3.mjs → ContactsScreen-BzRFTCBS.mjs} +2 -2
  5. package/dist/{ContactsScreen-Bm9SlMY3.mjs.map → ContactsScreen-BzRFTCBS.mjs.map} +1 -1
  6. package/dist/{FluidProvider-ByBDIQeW.cjs → FluidProvider-BRkRo8Wl.cjs} +8 -8
  7. package/dist/{FluidProvider-ByBDIQeW.cjs.map → FluidProvider-BRkRo8Wl.cjs.map} +1 -1
  8. package/dist/{FluidProvider-DFZiXiqm.mjs → FluidProvider-BTZAiT69.mjs} +8 -8
  9. package/dist/{FluidProvider-DFZiXiqm.mjs.map → FluidProvider-BTZAiT69.mjs.map} +1 -1
  10. package/dist/{MessagingScreen-D6d83q2n.mjs → MessagingScreen-Bk3Eh1dN.mjs} +2 -2
  11. package/dist/{MessagingScreen-D6d83q2n.mjs.map → MessagingScreen-Bk3Eh1dN.mjs.map} +1 -1
  12. package/dist/{MessagingScreen-HT_HSiNW.cjs → MessagingScreen-DN2eQRxF.cjs} +6 -6
  13. package/dist/{MessagingScreen-shEWzTmQ.cjs → MessagingScreen-DtDbS3VZ.cjs} +2 -2
  14. package/dist/{MessagingScreen-shEWzTmQ.cjs.map → MessagingScreen-DtDbS3VZ.cjs.map} +1 -1
  15. package/dist/{PortalContentApiProvider-ChmcXmbv.cjs → PortalContentApiProvider-BDbrZCyI.cjs} +3 -3
  16. package/dist/{PortalContentApiProvider-ChmcXmbv.cjs.map → PortalContentApiProvider-BDbrZCyI.cjs.map} +1 -1
  17. package/dist/{PortalContentApiProvider-CNYq1_OD.mjs → PortalContentApiProvider-CzLqEN5C.mjs} +3 -3
  18. package/dist/{PortalContentApiProvider-CNYq1_OD.mjs.map → PortalContentApiProvider-CzLqEN5C.mjs.map} +1 -1
  19. package/dist/{ProductsScreen-CzfDX0xx.cjs → ProductsScreen-68jB202M.cjs} +2 -2
  20. package/dist/{ProductsScreen-CzfDX0xx.cjs.map → ProductsScreen-68jB202M.cjs.map} +1 -1
  21. package/dist/{ProductsScreen-D6-ehQjh.mjs → ProductsScreen-BeNUsjh1.mjs} +2 -2
  22. package/dist/{ProductsScreen-CDjpHF49.mjs → ProductsScreen-BidL3ZF5.mjs} +2 -2
  23. package/dist/{ProductsScreen-CDjpHF49.mjs.map → ProductsScreen-BidL3ZF5.mjs.map} +1 -1
  24. package/dist/{ProductsScreen-fN3fh3PB.cjs → ProductsScreen-Bq0f4pQL.cjs} +2 -2
  25. package/dist/{ProfileScreen-Dqu2nK_2.cjs → ProfileScreen-BMNq0NEB.cjs} +6 -6
  26. package/dist/{ProfileScreen-BIs70k9W.mjs → ProfileScreen-D-pTegtY.mjs} +3 -3
  27. package/dist/{ProfileScreen-BIs70k9W.mjs.map → ProfileScreen-D-pTegtY.mjs.map} +1 -1
  28. package/dist/{ProfileScreen-B0EU-TLa.cjs → ProfileScreen-D5OxmzhM.cjs} +3 -3
  29. package/dist/{ProfileScreen-B0EU-TLa.cjs.map → ProfileScreen-D5OxmzhM.cjs.map} +1 -1
  30. package/dist/{QuickShareWidget-0GD4KWAr.cjs → QuickShareWidget-C_p3tPs5.cjs} +2 -2
  31. package/dist/QuickShareWidget-C_p3tPs5.cjs.map +1 -0
  32. package/dist/{QuickShareWidget-DZzrQjOx.mjs → QuickShareWidget-xKcV3ZQ5.mjs} +2 -2
  33. package/dist/QuickShareWidget-xKcV3ZQ5.mjs.map +1 -0
  34. package/dist/{ShareablesScreen-B8rPq-_7.mjs → ShareablesScreen-BRfgOnpL.mjs} +2 -2
  35. package/dist/{ShareablesScreen-B8rPq-_7.mjs.map → ShareablesScreen-BRfgOnpL.mjs.map} +1 -1
  36. package/dist/{ShareablesScreen-DFLAJxjs.cjs → ShareablesScreen-BYP65ZnU.cjs} +2 -2
  37. package/dist/{ShareablesScreen-DLXK1PAg.cjs → ShareablesScreen-CCqADUXE.cjs} +2 -2
  38. package/dist/{ShareablesScreen-DLXK1PAg.cjs.map → ShareablesScreen-CCqADUXE.cjs.map} +1 -1
  39. package/dist/{ShareablesScreen-Dc57L9m8.mjs → ShareablesScreen-YnNF0dD6.mjs} +2 -2
  40. package/dist/{ShopScreen-2yMsyFwk.mjs → ShopScreen-BOJGcSyG.mjs} +3 -3
  41. package/dist/{ShopScreen-2yMsyFwk.mjs.map → ShopScreen-BOJGcSyG.mjs.map} +1 -1
  42. package/dist/{ShopScreen-kk4yLzrW.cjs → ShopScreen-BzyBZ24D.cjs} +6 -6
  43. package/dist/{ShopScreen-BmHSLZ7H.cjs → ShopScreen-DeLp93hN.cjs} +3 -3
  44. package/dist/{ShopScreen-BmHSLZ7H.cjs.map → ShopScreen-DeLp93hN.cjs.map} +1 -1
  45. package/dist/{SpacerWidget-Da_sNa_X.mjs → SpacerWidget-BJFO-Xyh.mjs} +2 -2
  46. package/dist/SpacerWidget-BJFO-Xyh.mjs.map +1 -0
  47. package/dist/{SpacerWidget-CLFbkgoz.cjs → SpacerWidget-D9lOLPr5.cjs} +2 -2
  48. package/dist/SpacerWidget-D9lOLPr5.cjs.map +1 -0
  49. package/dist/{TableWidget-lKjTu7Go.cjs → TableWidget-C7qiWZc3.cjs} +1 -1
  50. package/dist/{TableWidget-B65hwjKS.mjs → TableWidget-DRByd9ig.mjs} +9 -9
  51. package/dist/TableWidget-DRByd9ig.mjs.map +1 -0
  52. package/dist/{TableWidget-FDbnEYZb.cjs → TableWidget-DUnz9hrD.cjs} +9 -9
  53. package/dist/TableWidget-DUnz9hrD.cjs.map +1 -0
  54. package/dist/index.cjs +24 -26
  55. package/dist/index.d.cts +2 -13
  56. package/dist/index.d.cts.map +1 -1
  57. package/dist/index.d.mts +2 -13
  58. package/dist/index.d.mts.map +1 -1
  59. package/dist/index.mjs +25 -25
  60. package/dist/{src-DvVPCD01.cjs → src-BNcNh8fM.cjs} +18 -93
  61. package/dist/src-BNcNh8fM.cjs.map +1 -0
  62. package/dist/{src-BRTXunU1.mjs → src-BjCPR0aG.mjs} +19 -82
  63. package/dist/src-BjCPR0aG.mjs.map +1 -0
  64. package/package.json +7 -7
  65. package/dist/QuickShareWidget-0GD4KWAr.cjs.map +0 -1
  66. package/dist/QuickShareWidget-DZzrQjOx.mjs.map +0 -1
  67. package/dist/SpacerWidget-CLFbkgoz.cjs.map +0 -1
  68. package/dist/SpacerWidget-Da_sNa_X.mjs.map +0 -1
  69. package/dist/TableWidget-B65hwjKS.mjs.map +0 -1
  70. package/dist/TableWidget-FDbnEYZb.cjs.map +0 -1
  71. package/dist/src-BRTXunU1.mjs.map +0 -1
  72. package/dist/src-DvVPCD01.cjs.map +0 -1
@@ -21,17 +21,6 @@ const SEMANTIC_COLOR_NAMES = [
21
21
  "muted",
22
22
  "destructive"
23
23
  ];
24
- const SHADE_STEPS = [
25
- 100,
26
- 200,
27
- 300,
28
- 400,
29
- 500,
30
- 600,
31
- 700,
32
- 800,
33
- 900
34
- ];
35
24
  const FONT_SIZE_KEYS = [
36
25
  "extraSmall",
37
26
  "small",
@@ -110,44 +99,6 @@ function getContrastingTextColor(background) {
110
99
  0
111
100
  ]), bg).toString({ format: "oklch" });
112
101
  }
113
- /**
114
- * Generate a 100–900 shade ramp from a base color.
115
- * Base anchors at 500. Light shades (100–400) step toward white,
116
- * dark shades (600–900) step toward black. Dark steps use an asymmetric
117
- * multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
118
- * initial descent. Chroma is nudged per step for perceptually natural ramps.
119
- */
120
- function generateShades(base) {
121
- const l = base.oklch.l ?? 0;
122
- const c = base.oklch.c ?? 0;
123
- const h = base.oklch.h ?? 0;
124
- const safeMax = l >= .885 ? .995 : .97;
125
- const safeMin = l <= .33 ? 0 : .21;
126
- const lightStep = (safeMax - l) / 5;
127
- const darkStep = -(l - safeMin) / 8;
128
- const shade = (lDelta, cDelta) => {
129
- return new colorjs_io.default("oklch", [
130
- Math.max(0, Math.min(1, l + lDelta)),
131
- c <= .001 ? c : Math.max(0, c + cDelta),
132
- h
133
- ]);
134
- };
135
- return {
136
- 100: shade(5 * lightStep, -.00375),
137
- 200: shade(4 * lightStep, -.00375),
138
- 300: shade(3 * lightStep, -.00375),
139
- 400: shade(2 * lightStep, -.00375),
140
- 500: new colorjs_io.default("oklch", [
141
- l,
142
- c,
143
- h
144
- ]),
145
- 600: shade(1.6 * darkStep, .025),
146
- 700: shade(1.875 * 2 * darkStep, .05),
147
- 800: shade(6 * darkStep, .075),
148
- 900: shade(8 * darkStep, .1)
149
- };
150
- }
151
102
  const DARK_DERIVATION_CONFIG = {
152
103
  background: {
153
104
  baseLightness: .15,
@@ -233,13 +184,9 @@ function resolveColorSet(colors) {
233
184
  const resolved = {};
234
185
  for (const name of SEMANTIC_COLOR_NAMES) {
235
186
  const input = colors[name];
236
- const shades = generateShades(input.base);
237
- const resolvedShades = {};
238
- for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
239
187
  resolved[name] = {
240
188
  base: input.base.clone(),
241
- foreground: input.foreground.clone(),
242
- shades: resolvedShades
189
+ foreground: input.foreground.clone()
243
190
  };
244
191
  }
245
192
  return resolved;
@@ -262,26 +209,18 @@ function resolveTheme(def) {
262
209
  }
263
210
  //#endregion
264
211
  //#region ../../platform/theme-engine/src/tailwind-overrides.ts
265
- /**
266
- * Specific overrides, otherwise all the overrides are generated using emitTailwindOverrides
267
- */
268
212
  const OVERRIDES = {
269
213
  "--color-gray-50": "var(--color-muted)",
270
- "--color-gray-100": "var(--color-muted-600)",
214
+ "--color-gray-100": "color-mix(in oklch, var(--color-muted), var(--color-foreground) 15%)",
271
215
  "--color-gray-200": "var(--color-border)"
272
216
  };
273
217
  /**
274
- * Returns the inverted shade for dark mode foreground colors.
275
- * In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
218
+ * Map Tailwind built-in color names to semantic theme colors using color-mix
219
+ * for shade interpolation. Each shade maps to a percentage of the semantic
220
+ * color mixed with transparent, so shades naturally adapt to both light and
221
+ * dark modes without inversion logic.
276
222
  */
277
- function getInvertedStep(shade) {
278
- const shadeIndex = SHADE_STEPS.indexOf(shade);
279
- return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
280
- }
281
- /**
282
- * Map semantic colors to Tailwind built-in color names.
283
- */
284
- function emitTailwindOverrides(darkMode = false) {
223
+ function emitTailwindOverrides() {
285
224
  const TAILWIND_COLOR_MAP = {
286
225
  gray: "foreground",
287
226
  red: "destructive",
@@ -301,15 +240,14 @@ function emitTailwindOverrides(darkMode = false) {
301
240
  900,
302
241
  950
303
242
  ];
304
- const SHADE_REMAP = {
305
- 50: 100,
306
- 950: 900
307
- };
308
243
  const lines = [];
309
244
  for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
310
- const step = SHADE_REMAP[shade] ?? shade;
311
245
  const override = OVERRIDES[`--color-${twName}-${shade}`];
312
- lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
246
+ if (override) lines.push(`--color-${twName}-${shade}: ${override};`);
247
+ else {
248
+ const percent = Math.max(10, Math.min(Math.round(shade / 10), 100));
249
+ lines.push(`--color-${twName}-${shade}: color-mix(in oklch, var(--color-${semantic}) ${percent}%, transparent);`);
250
+ }
313
251
  }
314
252
  lines.push("--color-white: var(--color-background);");
315
253
  lines.push("--color-black: var(--color-foreground);");
@@ -329,7 +267,7 @@ function camelToKebab(str) {
329
267
  return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
330
268
  }
331
269
  /**
332
- * Emit --color-{name}, --color-{name}-foreground, --color-{name}-{shade} vars.
270
+ * Emit --color-{name} and --color-{name}-foreground vars.
333
271
  * Uses --color- prefix to match portal-widgets/tailwind.config.ts.
334
272
  */
335
273
  function emitColorVars(colors) {
@@ -338,7 +276,6 @@ function emitColorVars(colors) {
338
276
  const color = colors[name];
339
277
  lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
340
278
  lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
341
- for (const step of SHADE_STEPS) lines.push(`--color-${name}-${step}: ${colorToCSS(color.shades[step])};`);
342
279
  }
343
280
  return lines;
344
281
  }
@@ -382,7 +319,7 @@ const globalCSSOverride = [
382
319
  "--sidebar-primary: var(--color-primary);",
383
320
  "--sidebar-foreground: var(--color-muted-foreground);",
384
321
  "--sidebar: var(--color-muted);",
385
- "--border: var(--color-background-600);",
322
+ "--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);",
386
323
  "--ring: var(--color-primary);",
387
324
  "--popover: var(--color-background);",
388
325
  "--popover-foreground: var(--color-foreground);",
@@ -402,7 +339,7 @@ const globalCSSOverride = [
402
339
  /**
403
340
  * Overrides for global tailwindcss for specifically dark mode.
404
341
  */
405
- const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
342
+ const globalDarkCSSOverride = ["--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);"];
406
343
  /**
407
344
  * Generate a complete CSS string for a resolved theme.
408
345
  * Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
@@ -421,14 +358,14 @@ function generateThemeCSS(theme, options = {}) {
421
358
  blocks.push(`${sel}[data-theme-mode="dark"] {`);
422
359
  blocks.push(...globalDarkCSSOverride);
423
360
  blocks.push(...emitColorVars(theme.dark));
424
- if (tw) blocks.push(...emitTailwindOverrides(true));
361
+ if (tw) blocks.push(...emitTailwindOverrides());
425
362
  blocks.push(`}`);
426
363
  if (!options.disableAutoTheme) {
427
364
  blocks.push(`@media (prefers-color-scheme: dark) {`);
428
365
  blocks.push(`${sel}:not([data-theme-mode]) {`);
429
366
  blocks.push(...globalDarkCSSOverride);
430
367
  blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
431
- if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
368
+ if (tw) blocks.push(...emitTailwindOverrides().map((l) => `${l}`));
432
369
  blocks.push(`}`);
433
370
  blocks.push(`}`);
434
371
  }
@@ -920,12 +857,6 @@ Object.defineProperty(exports, "SEMANTIC_COLOR_NAMES", {
920
857
  return SEMANTIC_COLOR_NAMES;
921
858
  }
922
859
  });
923
- Object.defineProperty(exports, "SHADE_STEPS", {
924
- enumerable: true,
925
- get: function() {
926
- return SHADE_STEPS;
927
- }
928
- });
929
860
  Object.defineProperty(exports, "applyTheme", {
930
861
  enumerable: true,
931
862
  get: function() {
@@ -950,12 +881,6 @@ Object.defineProperty(exports, "deserialiseTheme", {
950
881
  return deserialiseTheme;
951
882
  }
952
883
  });
953
- Object.defineProperty(exports, "generateShades", {
954
- enumerable: true,
955
- get: function() {
956
- return generateShades;
957
- }
958
- });
959
884
  Object.defineProperty(exports, "generateThemeCSS", {
960
885
  enumerable: true,
961
886
  get: function() {
@@ -1035,4 +960,4 @@ Object.defineProperty(exports, "useCountriesApi", {
1035
960
  }
1036
961
  });
1037
962
 
1038
- //# sourceMappingURL=src-DvVPCD01.cjs.map
963
+ //# sourceMappingURL=src-BNcNh8fM.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"src-BNcNh8fM.cjs","names":["Color","Color","Color"],"sources":["../../../store/core/src/countries-api-context.ts","../../../platform/theme-engine/src/types.ts","../../../platform/theme-engine/src/color-engine.ts","../../../platform/theme-engine/src/tailwind-overrides.ts","../../../platform/theme-engine/src/css-generator.ts","../../../platform/theme-engine/src/defaults.ts","../../../platform/theme-engine/src/serialisation.ts","../../../platform/theme-engine/src/transforms.ts","../../../platform/theme-engine/src/theme-applicator.ts"],"sourcesContent":["import { createContext, useContext, type Provider } from \"react\";\nimport type { CountriesApi } from \"./countries-api\";\n\nconst CountriesApiContext = createContext<CountriesApi | null>(null);\n\nexport const CountriesApiProvider: Provider<CountriesApi | null> =\n CountriesApiContext.Provider;\n\nexport function useCountriesApi(): CountriesApi {\n const api = useContext(CountriesApiContext);\n if (!api) {\n throw new Error(\n \"useCountriesApi must be used within a CountriesApiProvider\",\n );\n }\n return api;\n}\n","import type Color from \"colorjs.io\";\n\n// Semantic color names - matches portal-widgets tailwind.config.ts and field-types.ts\nexport const SEMANTIC_COLOR_NAMES = [\n \"background\",\n \"foreground\",\n \"primary\",\n \"secondary\",\n \"accent\",\n \"muted\",\n \"destructive\",\n] as const;\nexport type SemanticColorName = (typeof SEMANTIC_COLOR_NAMES)[number];\n\nexport const FONT_SIZE_KEYS = [\n \"extraSmall\",\n \"small\",\n \"regular\",\n \"large\",\n \"extraLarge\",\n \"giant\",\n] as const;\nexport type FontSizeKey = (typeof FONT_SIZE_KEYS)[number];\n\nexport const FONT_FAMILY_KEYS = [\"header\", \"body\"] as const;\nexport type FontFamilyKey = (typeof FONT_FAMILY_KEYS)[number];\n\nexport const RADIUS_KEYS = [\"small\", \"medium\", \"large\", \"extraLarge\"] as const;\nexport type RadiusKey = (typeof RADIUS_KEYS)[number];\n\n/** Author-time color input (what the user configures) */\nexport interface ThemeColorInput {\n base: Color;\n foreground: Color;\n}\n\n/** Complete theme definition — stored in-memory with Color objects */\nexport interface ThemeDefinition {\n id: string;\n name: string;\n /** Light mode — always fully specified */\n light: Record<SemanticColorName, ThemeColorInput>;\n /**\n * Dark mode — only user-overridden colors.\n * Missing keys are auto-derived from `light` at resolve time.\n */\n dark: Partial<Record<SemanticColorName, Partial<ThemeColorInput>>>;\n fontSizes: Record<FontSizeKey, string>;\n fontFamilies: Record<FontFamilyKey, string>;\n spacing: string;\n radii: Record<RadiusKey, string>;\n /** When true, theme colors are re-derived from brand guidelines on every load */\n syncWithBrandColors?: boolean;\n}\n\n/** Resolved semantic color */\nexport interface ResolvedSemanticColor {\n base: Color;\n foreground: Color;\n}\n\n/** Complete resolved color set for one mode */\nexport type ResolvedColorSet = Record<SemanticColorName, ResolvedSemanticColor>;\n\n/** Fully resolved theme — all colors materialised for both modes */\nexport interface ResolvedTheme {\n id: string;\n name: string;\n light: ResolvedColorSet;\n dark: ResolvedColorSet;\n fontSizes: ThemeDefinition[\"fontSizes\"];\n fontFamilies: ThemeDefinition[\"fontFamilies\"];\n spacing: string;\n radii: ThemeDefinition[\"radii\"];\n}\n\n/** Plain OKLCH triplet for JSON serialisation (no Color dependency) */\nexport interface OklchPlain {\n l: number;\n c: number;\n h: number;\n}\n\n/** Serialised color pair as stored in the backend payload */\nexport interface ThemeColorPlain {\n base: OklchPlain;\n foreground: OklchPlain;\n}\n\n/** Backend payload — plain JSON, no Color objects */\nexport interface ThemePayload {\n [key: string]: unknown;\n id: string;\n name: string;\n light: Record<SemanticColorName, ThemeColorPlain>;\n dark: Partial<\n Record<SemanticColorName, { base?: OklchPlain; foreground?: OklchPlain }>\n >;\n fontSizes: Record<FontSizeKey, string>;\n fontFamilies: Record<FontFamilyKey, string>;\n spacing: string;\n radii: Record<RadiusKey, string>;\n syncWithBrandColors?: boolean;\n}\n","import Color from \"colorjs.io\";\nimport {\n SEMANTIC_COLOR_NAMES,\n type SemanticColorName,\n type ThemeColorInput,\n type ThemeDefinition,\n type ResolvedColorSet,\n type ResolvedTheme,\n} from \"./types\";\n\nconst BARE_HEX_RE = /^[0-9a-fA-F]{6}$/;\n\n/**\n * Attempt to convert any string into a Color using colorjs.io.\n * If the string is exactly 6 hex digits it is assumed to be a bare hex value\n * (e.g. \"3b82f6\") and a \"#\" prefix is added before parsing. Six-letter\n * named colours like \"orange\" or \"maroon\" are left untouched.\n *\n * @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure\n */\nexport function parseColor(value: string): Color {\n if (BARE_HEX_RE.test(value)) {\n value = `#${value}`;\n }\n try {\n return new Color(value);\n } catch (error) {\n console.warn(\"[theme] Failed to parse color:\", value, error);\n return new Color(\"oklch\", [0.5, 0, 0]);\n }\n}\n\n/**\n * Returns either the original foreground or a corrected lightness variant,\n * whichever provides better contrast against `color`.\n * Inversion triggers when the |APCA contrast| is below 50 — APCA is signed\n * (negative for dark-on-light, positive for light-on-dark), so comparing the\n * absolute value avoids flipping dark text that already contrasts well on a\n * medium background.\n */\nexport function getForegroundColor(foreground: Color, color: Color): Color {\n if (foreground.oklch.l == null || color.oklch.l == null) {\n return foreground;\n }\n const contrast = color.contrastAPCA(foreground);\n\n if (Math.abs(contrast) < 50) {\n return new Color(\"oklch\", [\n color.oklch.l < 0.7 ? 0.95 : 0.15,\n foreground.oklch.c || 0,\n foreground.oklch.h || 0,\n ]);\n }\n return foreground;\n}\n\n/**\n * Convenience helper: given a background color string, return a CSS color\n * string for text overlaid on it. Uses APCA contrast to pick between\n * near-black and near-white. Returns `null` when the background cannot be\n * parsed (e.g. a CSS custom property reference like `var(--color-muted)` or\n * a malformed value), so callers can fall back to their theme foreground.\n */\nexport function getContrastingTextColor(background: string): string | null {\n const normalised = BARE_HEX_RE.test(background)\n ? `#${background}`\n : background;\n let bg: Color;\n try {\n bg = new Color(normalised);\n } catch {\n return null;\n }\n const initialFg = new Color(\"oklch\", [0.15, 0, 0]);\n return getForegroundColor(initialFg, bg).toString({ format: \"oklch\" });\n}\n\n// ── Dark Mode Derivation ────────────────────────────────────────────\n//\n// Dark-mode colors are derived from their light counterparts by adjusting\n// OKLCH lightness and optionally scaling chroma. Neutral slots (background,\n// foreground, muted) use fixed lightness values while chromatic slots\n// (primary, secondary, accent, destructive) invert lightness around 0.5.\n\nconst DARK_DERIVATION_CONFIG: Record<\n SemanticColorName,\n {\n baseLightness: number | \"invert\";\n fgLightness: number | \"invert\";\n chromaScale?: number;\n }\n> = {\n background: { baseLightness: 0.15, fgLightness: 0.93 },\n foreground: { baseLightness: 0.93, fgLightness: 0.15 },\n muted: { baseLightness: 0.22, fgLightness: 0.75 },\n primary: { baseLightness: \"invert\", fgLightness: 0.95, chromaScale: 0.9 },\n secondary: { baseLightness: \"invert\", fgLightness: 0.93, chromaScale: 0.85 },\n accent: { baseLightness: \"invert\", fgLightness: 0.95, chromaScale: 0.9 },\n destructive: {\n baseLightness: \"invert\",\n fgLightness: 0.95,\n chromaScale: 0.95,\n },\n};\n\n/** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */\nfunction invertLightness(l: number): number {\n const inverted = 1 - l;\n return Math.max(0.35, Math.min(0.75, inverted));\n}\n\n/**\n * Derive a dark-mode ThemeColorInput from its light-mode counterpart.\n */\nexport function deriveDarkVariant(\n name: SemanticColorName,\n light: ThemeColorInput,\n): ThemeColorInput {\n const config = DARK_DERIVATION_CONFIG[name];\n const chromaScale = config.chromaScale ?? 1;\n\n const baseLightness =\n config.baseLightness === \"invert\"\n ? invertLightness(light.base.oklch.l ?? 0)\n : config.baseLightness;\n\n const fgLightness =\n config.fgLightness === \"invert\"\n ? invertLightness(light.foreground.oklch.l ?? 0)\n : config.fgLightness;\n\n return {\n base: new Color(\"oklch\", [\n baseLightness,\n (light.base.oklch.c || 0) * chromaScale,\n light.base.oklch.h || 0,\n ]),\n foreground: new Color(\"oklch\", [\n fgLightness,\n (light.foreground.oklch.c || 0) * chromaScale,\n light.foreground.oklch.h || 0,\n ]),\n };\n}\n\n// ── Dark Mode Merge ─────────────────────────────────────────────────\n\n/**\n * Merge auto-derived dark colors with any user-specified overrides.\n * For each semantic color, if the user has fully overridden both base and\n * foreground those are used; otherwise the missing channels are derived.\n */\nexport function mergeDarkOverrides(\n def: ThemeDefinition,\n): Record<SemanticColorName, ThemeColorInput> {\n const darkColors = {} as Record<SemanticColorName, ThemeColorInput>;\n\n for (const name of SEMANTIC_COLOR_NAMES) {\n const lightInput = def.light[name];\n const darkOverride = def.dark[name];\n\n if (darkOverride?.base && darkOverride?.foreground) {\n darkColors[name] = darkOverride as ThemeColorInput;\n } else if (darkOverride) {\n const base =\n darkOverride.base ?? deriveDarkVariant(name, lightInput).base;\n darkColors[name] = {\n base: base,\n foreground:\n darkOverride.foreground ??\n getForegroundColor(def.light.foreground.base, base),\n };\n } else {\n darkColors[name] = deriveDarkVariant(name, lightInput);\n }\n }\n\n return darkColors;\n}\n\n// ── Theme Resolution ────────────────────────────────────────────────\n\nfunction resolveColorSet(\n colors: Record<SemanticColorName, ThemeColorInput>,\n): ResolvedColorSet {\n const resolved = {} as ResolvedColorSet;\n\n for (const name of SEMANTIC_COLOR_NAMES) {\n const input = colors[name];\n resolved[name] = {\n base: input.base.clone(),\n foreground: input.foreground.clone(),\n };\n }\n\n return resolved;\n}\n\n/**\n * Resolve a ThemeDefinition into a complete ResolvedTheme.\n * Dark mode colors are derived from light where not overridden.\n */\nexport function resolveTheme(def: ThemeDefinition): ResolvedTheme {\n return {\n id: def.id,\n name: def.name,\n light: resolveColorSet(def.light),\n dark: resolveColorSet(mergeDarkOverrides(def)),\n fontSizes: { ...def.fontSizes },\n fontFamilies: { ...def.fontFamilies },\n spacing: def.spacing,\n radii: { ...def.radii },\n };\n}\n","import type { SemanticColorName } from \"./types\";\n\nconst OVERRIDES: Partial<Record<string, string>> = {\n \"--color-gray-50\": \"var(--color-muted)\",\n \"--color-gray-100\":\n \"color-mix(in oklch, var(--color-muted), var(--color-foreground) 15%)\",\n \"--color-gray-200\": \"var(--color-border)\",\n} as const;\n\n/**\n * Map Tailwind built-in color names to semantic theme colors using color-mix\n * for shade interpolation. Each shade maps to a percentage of the semantic\n * color mixed with transparent, so shades naturally adapt to both light and\n * dark modes without inversion logic.\n */\nexport function emitTailwindOverrides(): string[] {\n const TAILWIND_COLOR_MAP: Record<string, SemanticColorName> = {\n gray: \"foreground\",\n red: \"destructive\",\n blue: \"primary\",\n green: \"accent\",\n };\n\n const TAILWIND_SHADES = [\n 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950,\n ] as const;\n\n const lines: string[] = [];\n for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) {\n for (const shade of TAILWIND_SHADES) {\n const override = OVERRIDES[`--color-${twName}-${shade}`];\n if (override) {\n lines.push(`--color-${twName}-${shade}: ${override};`);\n } else {\n const percent = Math.max(10, Math.min(Math.round(shade / 10), 100));\n lines.push(\n `--color-${twName}-${shade}: color-mix(in oklch, var(--color-${semantic}) ${percent}%, transparent);`,\n );\n }\n }\n }\n\n lines.push(\"--color-white: var(--color-background);\");\n lines.push(\"--color-black: var(--color-foreground);\");\n\n return lines;\n}\n","import { emitTailwindOverrides } from \"./tailwind-overrides\";\nimport {\n SEMANTIC_COLOR_NAMES,\n FONT_SIZE_KEYS,\n FONT_FAMILY_KEYS,\n RADIUS_KEYS,\n type ResolvedColorSet,\n type ResolvedTheme,\n} from \"./types\";\n\nfunction colorToCSS(color: import(\"colorjs.io\").default): string {\n const result = color.toString({ format: \"oklch\" });\n if (result.includes(\"NaN\")) {\n console.warn(\n \"[theme] colorToCSS produced NaN, using neutral fallback:\",\n result,\n );\n return \"oklch(0.5 0 0)\";\n }\n return result;\n}\n\nfunction camelToKebab(str: string): string {\n return str.replace(/([a-z])([A-Z])/g, \"$1-$2\").toLowerCase();\n}\n\n/**\n * Emit --color-{name} and --color-{name}-foreground vars.\n * Uses --color- prefix to match portal-widgets/tailwind.config.ts.\n */\nfunction emitColorVars(colors: ResolvedColorSet): string[] {\n const lines: string[] = [];\n\n for (const name of SEMANTIC_COLOR_NAMES) {\n const color = colors[name];\n lines.push(`--color-${name}: ${colorToCSS(color.base)};`);\n lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);\n }\n\n return lines;\n}\n\n/**\n * Format a font family value for CSS output.\n * - If the value starts with \"var(\" (legacy), pass through as-is\n * - If the value already contains a comma (has fallback), pass through as-is\n * - Otherwise, wrap in quotes and append a generic sans-serif fallback\n */\nfunction formatFontFamily(value: string): string {\n if (value.startsWith(\"var(\")) return value;\n if (value.includes(\",\")) return value;\n return `'${value}', sans-serif`;\n}\n\n/**\n * Emit non-color CSS variables (font sizes, families, spacing, radii).\n */\nfunction emitNonColorVars(theme: ResolvedTheme): string[] {\n const lines: string[] = [];\n for (const key of FONT_SIZE_KEYS) {\n lines.push(`--font-size-${camelToKebab(key)}: ${theme.fontSizes[key]};`);\n }\n for (const key of FONT_FAMILY_KEYS) {\n lines.push(`--font-${key}: ${formatFontFamily(theme.fontFamilies[key])};`);\n }\n lines.push(`--spacing: ${theme.spacing};`);\n for (const key of RADIUS_KEYS) {\n lines.push(`--radius-${camelToKebab(key)}: ${theme.radii[key]};`);\n }\n return lines;\n}\n\n/**\n * Static CSS alias variables that bridge theme var names to Tailwind/component conventions.\n * These are always emitted and not mode-dependent.\n */\nconst globalCSSOverride = [\n \"--color-background-foreground: var(--color-foreground);\",\n \"--color-foreground-foreground: var(--color-background);\",\n \"--color-contrast: var(--color-foreground);\",\n ...SEMANTIC_COLOR_NAMES.map((value) => `--${value}: var(--color-${value});`),\n ...SEMANTIC_COLOR_NAMES.map(\n (value) => `--${value}-foreground: var(--color-${value}-foreground);`,\n ),\n\n \"--sidebar-ring: var(--color-primary);\",\n \"--sidebar-border: var(--color-border);\",\n \"--sidebar-accent-foreground: var(--color-accent-foreground);\",\n \"--sidebar-accent: var(--color-accent);\",\n \"--sidebar-primary-foreground: var(--color-primary-foreground);\",\n \"--sidebar-primary: var(--color-primary);\",\n \"--sidebar-foreground: var(--color-muted-foreground);\",\n \"--sidebar: var(--color-muted);\",\n \"--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);\",\n \"--ring: var(--color-primary);\",\n \"--popover: var(--color-background);\",\n \"--popover-foreground: var(--color-foreground);\",\n \"--card: var(--color-muted);\",\n \"--card-foreground: var(--color-muted-foreground);\",\n\n \"--radius-sm: var(--radius-small);\",\n \"--radius-md: var(--radius-medium);\",\n \"--radius-lg: var(--radius-large);\",\n \"--radius-xl: var(--radius-extra-large);\",\n \"--text-xs: var(--font-size-extra-small);\",\n \"--text-sm: var(--font-size-small);\",\n \"--text-base: var(--font-size-regular);\",\n \"--text-lg: var(--font-size-large);\",\n \"--text-xl: var(--font-size-extra-large);\",\n \"--text-2xl: var(--font-size-giant);\",\n];\n\n/**\n * Overrides for global tailwindcss for specifically dark mode.\n */\nconst globalDarkCSSOverride = [\n \"--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);\",\n];\n\nexport interface GenerateThemeCSSOptions {\n /** Whether or not to allow prefers-color-scheme to choose the theme mode */\n disableAutoTheme?: boolean;\n /** Whether to emit Tailwind built-in color overrides (default true) */\n mapTailwindColors?: boolean;\n}\n\n/**\n * Generate a complete CSS string for a resolved theme.\n * Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode=\"dark\"]`,\n * and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.\n */\nexport function generateThemeCSS(\n theme: ResolvedTheme,\n options: GenerateThemeCSSOptions = {},\n): string {\n const sel = `[data-theme=\"${theme.id}\"]`;\n const tw = options.mapTailwindColors ?? true;\n const blocks: string[] = [];\n\n // Light mode (default)\n blocks.push(`${sel} {`);\n blocks.push(...globalCSSOverride);\n blocks.push(...emitNonColorVars(theme));\n blocks.push(...emitColorVars(theme.light));\n if (tw) blocks.push(...emitTailwindOverrides());\n blocks.push(`}`);\n\n // Dark mode: explicit via attribute\n blocks.push(`${sel}[data-theme-mode=\"dark\"] {`);\n blocks.push(...globalDarkCSSOverride);\n blocks.push(...emitColorVars(theme.dark));\n if (tw) blocks.push(...emitTailwindOverrides());\n blocks.push(`}`);\n\n // Dark mode: auto via system preference\n if (!options.disableAutoTheme) {\n blocks.push(`@media (prefers-color-scheme: dark) {`);\n blocks.push(`${sel}:not([data-theme-mode]) {`);\n blocks.push(...globalDarkCSSOverride);\n blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));\n if (tw) blocks.push(...emitTailwindOverrides().map((l) => `${l}`));\n blocks.push(`}`);\n blocks.push(`}`);\n }\n\n return blocks.join(\"\\n\");\n}\n","import Color from \"colorjs.io\";\nimport type {\n FontSizeKey,\n FontFamilyKey,\n RadiusKey,\n ThemeDefinition,\n} from \"./types\";\nimport { getForegroundColor } from \"./color-engine\";\n\n// ── Non-color defaults ──────────────────────────────────────────────\n\nexport const DEFAULT_FONT_SIZES: Record<FontSizeKey, string> = {\n extraSmall: \"0.75rem\",\n small: \"0.875rem\",\n regular: \"1rem\",\n large: \"1.125rem\",\n extraLarge: \"1.25rem\",\n giant: \"1.5rem\",\n};\n\nexport const DEFAULT_FONT_FAMILIES: Record<FontFamilyKey, string> = {\n header: \"Inter\",\n body: \"Inter\",\n};\n\nexport const DEFAULT_SPACING = \"0.25rem\";\n\nexport const DEFAULT_RADII: Record<RadiusKey, string> = {\n small: \"0.25rem\",\n medium: \"0.5rem\",\n large: \"0.75rem\",\n extraLarge: \"1rem\",\n};\n\n// ── Default colors (hex) ────────────────────────────────────────────\n\nexport const DEFAULT_COLORS = {\n background: \"#ffffff\",\n foreground: \"#1a1a1a\",\n primary: \"#3b82f6\",\n secondary: \"#6b7280\",\n accent: \"#10b981\",\n muted: \"#f3f4f6\",\n destructive: \"#ef4444\",\n mutedForeground: \"#6b7280\",\n} as const;\n\n// ── Default theme identity ──────────────────────────────────────────\n\nexport const DEFAULT_THEME_ID = \"default\";\nexport const DEFAULT_THEME_NAME = \"Default Theme\";\n\n// ── Factory ─────────────────────────────────────────────────────────\n\n/**\n * Build a fresh ThemeDefinition populated with all defaults.\n * Returns a new object each call because Color instances are mutable — do not cache the result.\n */\nexport function getDefaultThemeDefinition(): ThemeDefinition {\n const bg = new Color(DEFAULT_COLORS.background);\n const fg = new Color(DEFAULT_COLORS.foreground);\n const primary = new Color(DEFAULT_COLORS.primary);\n const secondary = new Color(DEFAULT_COLORS.secondary);\n const accent = new Color(DEFAULT_COLORS.accent);\n const muted = new Color(DEFAULT_COLORS.muted);\n const destructive = new Color(DEFAULT_COLORS.destructive);\n const mutedFg = new Color(DEFAULT_COLORS.mutedForeground);\n\n const darkBg = new Color(\"#0a0a0a\");\n const darkFg = new Color(\"#fafafa\");\n const darkMuted = new Color(\"#171717\");\n const darkMutedForeground = new Color(\"#dddddd\");\n\n return {\n id: DEFAULT_THEME_ID,\n name: DEFAULT_THEME_NAME,\n light: {\n background: { base: bg, foreground: fg },\n foreground: { base: fg, foreground: bg },\n primary: {\n base: primary,\n foreground: getForegroundColor(fg, primary),\n },\n secondary: {\n base: secondary,\n foreground: getForegroundColor(fg, secondary),\n },\n accent: {\n base: accent,\n foreground: getForegroundColor(fg, accent),\n },\n muted: { base: muted, foreground: mutedFg },\n destructive: {\n base: destructive,\n foreground: getForegroundColor(fg, destructive),\n },\n },\n dark: {\n background: { base: darkBg, foreground: darkFg },\n foreground: { base: darkFg, foreground: darkBg },\n muted: { base: darkMuted, foreground: darkMutedForeground },\n },\n fontSizes: { ...DEFAULT_FONT_SIZES },\n fontFamilies: { ...DEFAULT_FONT_FAMILIES },\n spacing: DEFAULT_SPACING,\n radii: { ...DEFAULT_RADII },\n };\n}\n","import Color from \"colorjs.io\";\nimport {\n SEMANTIC_COLOR_NAMES,\n type SemanticColorName,\n type ThemeColorInput,\n type ThemeDefinition,\n type ThemePayload,\n type OklchPlain,\n type FontSizeKey,\n type FontFamilyKey,\n type RadiusKey,\n} from \"./types\";\nimport {\n DEFAULT_FONT_SIZES,\n DEFAULT_FONT_FAMILIES,\n DEFAULT_SPACING,\n DEFAULT_RADII,\n getDefaultThemeDefinition,\n} from \"./defaults\";\n\nfunction colorToPlain(color: Color): OklchPlain {\n return {\n l: color.oklch.l ?? 0,\n c: color.oklch.c ?? 0,\n h: color.oklch.h ?? 0,\n };\n}\n\nfunction plainToColor(plain: OklchPlain): Color {\n return new Color(\"oklch\", [plain.l, plain.c, plain.h]);\n}\n\n/**\n * Serialise a ThemeDefinition (with Color objects) to a plain JSON payload\n * suitable for backend storage.\n */\nexport function serialiseTheme(def: ThemeDefinition): ThemePayload {\n const light = {} as ThemePayload[\"light\"];\n for (const name of SEMANTIC_COLOR_NAMES) {\n light[name] = {\n base: colorToPlain(def.light[name].base),\n foreground: colorToPlain(def.light[name].foreground),\n };\n }\n\n const dark: ThemePayload[\"dark\"] = {};\n for (const [name, value] of Object.entries(def.dark)) {\n if (!value) continue;\n dark[name as SemanticColorName] = {\n ...(value.base ? { base: colorToPlain(value.base) } : {}),\n ...(value.foreground\n ? { foreground: colorToPlain(value.foreground) }\n : {}),\n };\n }\n\n return {\n id: def.id,\n name: def.name,\n light,\n dark,\n fontSizes: { ...def.fontSizes },\n fontFamilies: { ...def.fontFamilies },\n spacing: def.spacing,\n radii: { ...def.radii },\n ...(def.syncWithBrandColors ? { syncWithBrandColors: true } : {}),\n };\n}\n\n/**\n * Deserialise a backend payload into a ThemeDefinition with Color objects.\n * Accepts `Record<string, unknown>` because API data is untyped at the boundary.\n * Falls back to default colors for any missing light-mode entries.\n */\nexport function deserialiseTheme(\n payload: Record<string, unknown>,\n): ThemeDefinition {\n const lightRaw = ((payload.light as Record<string, unknown> | undefined) ??\n {}) as Record<string, { base: OklchPlain; foreground: OklchPlain }>;\n const darkRaw = ((payload.dark as Record<string, unknown> | undefined) ??\n {}) as Record<string, { base?: OklchPlain; foreground?: OklchPlain }>;\n\n const defaults = getDefaultThemeDefinition();\n const light = {} as Record<SemanticColorName, ThemeColorInput>;\n for (const name of SEMANTIC_COLOR_NAMES) {\n const entry = lightRaw[name];\n if (entry) {\n light[name] = {\n base: plainToColor(entry.base),\n foreground: plainToColor(entry.foreground),\n };\n } else {\n console.warn(\n `[theme] deserialiseTheme: missing light color \"${name}\", using default`,\n );\n light[name] = defaults.light[name];\n }\n }\n\n const dark: Partial<Record<SemanticColorName, Partial<ThemeColorInput>>> = {};\n for (const [name, value] of Object.entries(darkRaw)) {\n if (!value) continue;\n dark[name as SemanticColorName] = {\n ...(value.base ? { base: plainToColor(value.base) } : {}),\n ...(value.foreground\n ? { foreground: plainToColor(value.foreground) }\n : {}),\n };\n }\n\n return {\n id: payload.id as string,\n name: payload.name as string,\n light,\n dark,\n fontSizes: (payload.fontSizes ?? DEFAULT_FONT_SIZES) as Record<\n FontSizeKey,\n string\n >,\n fontFamilies: (payload.fontFamilies ?? DEFAULT_FONT_FAMILIES) as Record<\n FontFamilyKey,\n string\n >,\n spacing: (payload.spacing as string) ?? DEFAULT_SPACING,\n radii: (payload.radii ?? DEFAULT_RADII) as Record<RadiusKey, string>,\n ...(payload.syncWithBrandColors === true\n ? { syncWithBrandColors: true }\n : {}),\n };\n}\n","/**\n * Theme Transforms\n * Convert raw API theme objects to ThemeDefinition format.\n * Handles both new structured format (OKLCH) and legacy flat format (hex strings).\n */\n\nimport type { ThemeDefinition } from \"./types\";\nimport { deserialiseTheme } from \"./serialisation\";\nimport { parseColor, getForegroundColor } from \"./color-engine\";\nimport {\n DEFAULT_COLORS,\n DEFAULT_FONT_SIZES,\n DEFAULT_FONT_FAMILIES,\n DEFAULT_SPACING,\n DEFAULT_RADII,\n} from \"./defaults\";\n\n/** Shape of a raw theme from the FluidOS API */\nexport interface RawApiTheme {\n id: number;\n config?: Record<string, unknown> | null;\n active?: boolean | null;\n name?: string | null;\n}\n\n/**\n * Check if a theme config uses the new structured format (has a `light` key\n * that is an object) vs the legacy flat format.\n */\nfunction isNewThemeFormat(config: Record<string, unknown>): boolean {\n return config.light != null && typeof config.light === \"object\";\n}\n\n/**\n * Convert a legacy flat config to a ThemeDefinition.\n * Legacy format: { base: \"#fff\", text: \"#000\", primary: \"oklch(0.6 0.2 250)\", ... }\n */\nfunction legacyConfigToDefinition(\n id: number,\n name: string,\n config: Record<string, string>,\n): ThemeDefinition {\n const bg = parseColor(\n config.base ?? config.background ?? DEFAULT_COLORS.background,\n );\n const fg = parseColor(\n config.text ?? config.foreground ?? DEFAULT_COLORS.foreground,\n );\n const primary = parseColor(config.primary ?? DEFAULT_COLORS.primary);\n const secondary = parseColor(config.secondary ?? DEFAULT_COLORS.secondary);\n const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);\n const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);\n const destructive = parseColor(\n config.destructive ?? DEFAULT_COLORS.destructive,\n );\n const mutedFg = parseColor(\n config.mutedForeground ?? DEFAULT_COLORS.mutedForeground,\n );\n\n return {\n id: String(id),\n name,\n light: {\n background: { base: bg, foreground: fg },\n foreground: { base: fg, foreground: bg },\n primary: {\n base: primary,\n foreground: getForegroundColor(fg, primary),\n },\n secondary: {\n base: secondary,\n foreground: getForegroundColor(fg, secondary),\n },\n accent: {\n base: accent,\n foreground: getForegroundColor(fg, accent),\n },\n muted: { base: muted, foreground: mutedFg },\n destructive: {\n base: destructive,\n foreground: getForegroundColor(fg, destructive),\n },\n },\n dark: {},\n fontSizes: {\n extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,\n small: config.small ?? DEFAULT_FONT_SIZES.small,\n regular: config.regular ?? DEFAULT_FONT_SIZES.regular,\n large: config.large ?? DEFAULT_FONT_SIZES.large,\n extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,\n giant: config.giant ?? DEFAULT_FONT_SIZES.giant,\n },\n fontFamilies: {\n header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,\n body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body,\n },\n spacing: config.globalSpacing ?? DEFAULT_SPACING,\n radii: {\n small: config.radiusSmall ?? DEFAULT_RADII.small,\n medium: config.radiusMedium ?? DEFAULT_RADII.medium,\n large: config.radiusLarge ?? DEFAULT_RADII.large,\n extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge,\n },\n };\n}\n\n/**\n * Build a ThemeDefinition from a single API theme object.\n * Handles both new structured format and legacy flat format.\n */\nexport function buildThemeDefinition(theme: RawApiTheme): ThemeDefinition {\n const config = (theme.config ?? {}) as Record<string, unknown>;\n\n if (isNewThemeFormat(config)) {\n return deserialiseTheme({\n ...config,\n id: String(theme.id),\n name: theme.name ?? \"Untitled Theme\",\n });\n }\n\n return legacyConfigToDefinition(\n theme.id,\n theme.name ?? \"Untitled Theme\",\n config as Record<string, string>,\n );\n}\n\n/**\n * Transform raw API themes to ThemeDefinition[].\n * Catches and logs errors per theme (graceful degradation).\n */\nexport function transformThemes(themes: RawApiTheme[]): ThemeDefinition[] {\n return themes.flatMap((theme) => {\n try {\n return [buildThemeDefinition(theme)];\n } catch (error) {\n console.error(`[theme] Failed to build theme id=${theme.id}:`, error);\n return [];\n }\n });\n}\n\n/**\n * Get the active theme ID from a list of raw API themes.\n * Falls back to the first theme if none is marked active.\n */\nexport function getActiveThemeId(themes: RawApiTheme[]): string | undefined {\n const active = themes.find((t) => t.active) ?? themes[0];\n return active ? String(active.id) : undefined;\n}\n","import { generateThemeCSS } from \"./css-generator\";\nimport { FONT_FAMILY_KEYS, type ResolvedTheme } from \"./types\";\nimport type { GenerateThemeCSSOptions } from \"./css-generator\";\n\nconst STYLE_PREFIX = \"theme-style-\";\nconst FONT_LINK_PREFIX = \"theme-font-\";\n\nconst SYSTEM_FONTS = new Set([\n \"sans-serif\",\n \"serif\",\n \"monospace\",\n \"cursive\",\n \"fantasy\",\n \"system-ui\",\n \"ui-sans-serif\",\n \"ui-serif\",\n \"ui-monospace\",\n]);\n\n/** Build a Google Fonts CSS2 URL for a given font family with all weights. */\nfunction buildGoogleFontUrl(family: string): string {\n const encoded = encodeURIComponent(family).replace(/%20/g, \"+\");\n return `https://fonts.googleapis.com/css2?family=${encoded}:wght@100;200;300;400;500;600;700;800;900&display=swap`;\n}\n\n/** Check if a font family value needs to be loaded (i.e. is not a CSS var or system font). */\nfunction isLoadableFont(value: string): boolean {\n if (!value) return false;\n if (value.startsWith(\"var(\")) return false;\n return !SYSTEM_FONTS.has(value.toLowerCase());\n}\n\n/** Deterministic link element ID for a font family. */\nfunction fontLinkId(family: string): string {\n return `${FONT_LINK_PREFIX}${family.replace(/\\s+/g, \"-\").toLowerCase()}`;\n}\n\n/**\n * Inject or update `<link>` elements for Google Fonts used by the theme.\n * Removes links for fonts that are no longer referenced.\n */\nfunction loadThemeFonts(theme: ResolvedTheme): void {\n if (typeof document === \"undefined\") return;\n\n const fontsToLoad = new Set<string>();\n for (const key of FONT_FAMILY_KEYS) {\n const value = theme.fontFamilies[key];\n if (isLoadableFont(value)) {\n fontsToLoad.add(value);\n }\n }\n\n // Remove stale font links owned by this theme\n const existingLinks = document.querySelectorAll(\n `link[id^=\"${FONT_LINK_PREFIX}\"]`,\n );\n existingLinks.forEach((link) => {\n const owners = link.getAttribute(\"data-font-theme-ids\")?.split(\",\") ?? [];\n if (!owners.includes(theme.id)) return;\n const fontName = link.getAttribute(\"data-font-family\");\n if (fontName && !fontsToLoad.has(fontName)) {\n const remaining = owners.filter((id) => id !== theme.id);\n if (remaining.length === 0) {\n link.remove();\n } else {\n link.setAttribute(\"data-font-theme-ids\", remaining.join(\",\"));\n }\n }\n });\n\n // Add new font links (or register this theme as an owner of existing ones)\n for (const family of fontsToLoad) {\n const id = fontLinkId(family);\n const existing = document.getElementById(id);\n if (existing) {\n const owners =\n existing.getAttribute(\"data-font-theme-ids\")?.split(\",\") ?? [];\n if (!owners.includes(theme.id)) {\n existing.setAttribute(\n \"data-font-theme-ids\",\n [...owners, theme.id].join(\",\"),\n );\n }\n } else {\n const link = document.createElement(\"link\");\n link.id = id;\n link.rel = \"stylesheet\";\n link.href = buildGoogleFontUrl(family);\n link.setAttribute(\"data-font-family\", family);\n link.setAttribute(\"data-font-theme-ids\", theme.id);\n document.head.appendChild(link);\n }\n }\n}\n\n/** Remove all font `<link>` elements injected by the theme system. */\nfunction removeAllFontLinks(): void {\n if (typeof document === \"undefined\") return;\n document\n .querySelectorAll(`link[id^=\"${FONT_LINK_PREFIX}\"]`)\n .forEach((el) => el.remove());\n}\n\n/**\n * Inject or update a `<style>` element in `<head>` for the given theme.\n * The element ID is deterministic (`theme-style-{themeId}`) so repeated calls\n * for the same theme are idempotent — the existing element is updated in place.\n * Also loads Google Fonts referenced by the theme's font families.\n * No-op when `document` is unavailable (SSR).\n */\nexport function applyTheme(\n theme: ResolvedTheme,\n options?: GenerateThemeCSSOptions,\n): void {\n if (typeof document === \"undefined\") return;\n\n try {\n loadThemeFonts(theme);\n\n const styleId = `${STYLE_PREFIX}${theme.id}`;\n let el = document.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!el) {\n el = document.createElement(\"style\");\n el.id = styleId;\n document.head.appendChild(el);\n }\n\n el.textContent = generateThemeCSS(theme, options);\n } catch (error) {\n console.error(`[theme] applyTheme failed for \"${theme.id}\":`, error);\n }\n}\n\n/** Remove an injected theme stylesheet and clean up font link ownership. No-op during SSR. */\nexport function removeTheme(themeId: string): void {\n if (typeof document === \"undefined\") return;\n document.getElementById(`${STYLE_PREFIX}${themeId}`)?.remove();\n\n // Remove this theme from font link ownership; delete links with no remaining owners\n document\n .querySelectorAll(`link[id^=\"${FONT_LINK_PREFIX}\"]`)\n .forEach((link) => {\n const owners = link.getAttribute(\"data-font-theme-ids\")?.split(\",\") ?? [];\n const remaining = owners.filter((id) => id !== themeId);\n if (remaining.length === 0) {\n link.remove();\n } else {\n link.setAttribute(\"data-font-theme-ids\", remaining.join(\",\"));\n }\n });\n}\n\n/** Remove all injected theme stylesheets and font links. No-op during SSR. */\nexport function removeAllThemes(): void {\n if (typeof document === \"undefined\") return;\n document\n .querySelectorAll(`style[id^=\"${STYLE_PREFIX}\"]`)\n .forEach((el) => el.remove());\n removeAllFontLinks();\n}\n"],"mappings":"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"}
@@ -19,17 +19,6 @@ const SEMANTIC_COLOR_NAMES = [
19
19
  "muted",
20
20
  "destructive"
21
21
  ];
22
- const SHADE_STEPS = [
23
- 100,
24
- 200,
25
- 300,
26
- 400,
27
- 500,
28
- 600,
29
- 700,
30
- 800,
31
- 900
32
- ];
33
22
  const FONT_SIZE_KEYS = [
34
23
  "extraSmall",
35
24
  "small",
@@ -108,44 +97,6 @@ function getContrastingTextColor(background) {
108
97
  0
109
98
  ]), bg).toString({ format: "oklch" });
110
99
  }
111
- /**
112
- * Generate a 100–900 shade ramp from a base color.
113
- * Base anchors at 500. Light shades (100–400) step toward white,
114
- * dark shades (600–900) step toward black. Dark steps use an asymmetric
115
- * multiplier (1.6×, 1.875×, 3×, 4× of `darkStep`) for a more gradual
116
- * initial descent. Chroma is nudged per step for perceptually natural ramps.
117
- */
118
- function generateShades(base) {
119
- const l = base.oklch.l ?? 0;
120
- const c = base.oklch.c ?? 0;
121
- const h = base.oklch.h ?? 0;
122
- const safeMax = l >= .885 ? .995 : .97;
123
- const safeMin = l <= .33 ? 0 : .21;
124
- const lightStep = (safeMax - l) / 5;
125
- const darkStep = -(l - safeMin) / 8;
126
- const shade = (lDelta, cDelta) => {
127
- return new Color("oklch", [
128
- Math.max(0, Math.min(1, l + lDelta)),
129
- c <= .001 ? c : Math.max(0, c + cDelta),
130
- h
131
- ]);
132
- };
133
- return {
134
- 100: shade(5 * lightStep, -.00375),
135
- 200: shade(4 * lightStep, -.00375),
136
- 300: shade(3 * lightStep, -.00375),
137
- 400: shade(2 * lightStep, -.00375),
138
- 500: new Color("oklch", [
139
- l,
140
- c,
141
- h
142
- ]),
143
- 600: shade(1.6 * darkStep, .025),
144
- 700: shade(1.875 * 2 * darkStep, .05),
145
- 800: shade(6 * darkStep, .075),
146
- 900: shade(8 * darkStep, .1)
147
- };
148
- }
149
100
  const DARK_DERIVATION_CONFIG = {
150
101
  background: {
151
102
  baseLightness: .15,
@@ -231,13 +182,9 @@ function resolveColorSet(colors) {
231
182
  const resolved = {};
232
183
  for (const name of SEMANTIC_COLOR_NAMES) {
233
184
  const input = colors[name];
234
- const shades = generateShades(input.base);
235
- const resolvedShades = {};
236
- for (const step of SHADE_STEPS) resolvedShades[step] = shades[step];
237
185
  resolved[name] = {
238
186
  base: input.base.clone(),
239
- foreground: input.foreground.clone(),
240
- shades: resolvedShades
187
+ foreground: input.foreground.clone()
241
188
  };
242
189
  }
243
190
  return resolved;
@@ -260,26 +207,18 @@ function resolveTheme(def) {
260
207
  }
261
208
  //#endregion
262
209
  //#region ../../platform/theme-engine/src/tailwind-overrides.ts
263
- /**
264
- * Specific overrides, otherwise all the overrides are generated using emitTailwindOverrides
265
- */
266
210
  const OVERRIDES = {
267
211
  "--color-gray-50": "var(--color-muted)",
268
- "--color-gray-100": "var(--color-muted-600)",
212
+ "--color-gray-100": "color-mix(in oklch, var(--color-muted), var(--color-foreground) 15%)",
269
213
  "--color-gray-200": "var(--color-border)"
270
214
  };
271
215
  /**
272
- * Returns the inverted shade for dark mode foreground colors.
273
- * In dark mode, light shades (50, 100) should map to dark values (950, 900) and vice versa.
274
- */
275
- function getInvertedStep(shade) {
276
- const shadeIndex = SHADE_STEPS.indexOf(shade);
277
- return SHADE_STEPS[SHADE_STEPS.length - 1 - shadeIndex] || 500;
278
- }
279
- /**
280
- * Map semantic colors to Tailwind built-in color names.
216
+ * Map Tailwind built-in color names to semantic theme colors using color-mix
217
+ * for shade interpolation. Each shade maps to a percentage of the semantic
218
+ * color mixed with transparent, so shades naturally adapt to both light and
219
+ * dark modes without inversion logic.
281
220
  */
282
- function emitTailwindOverrides(darkMode = false) {
221
+ function emitTailwindOverrides() {
283
222
  const TAILWIND_COLOR_MAP = {
284
223
  gray: "foreground",
285
224
  red: "destructive",
@@ -299,15 +238,14 @@ function emitTailwindOverrides(darkMode = false) {
299
238
  900,
300
239
  950
301
240
  ];
302
- const SHADE_REMAP = {
303
- 50: 100,
304
- 950: 900
305
- };
306
241
  const lines = [];
307
242
  for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
308
- const step = SHADE_REMAP[shade] ?? shade;
309
243
  const override = OVERRIDES[`--color-${twName}-${shade}`];
310
- lines.push(`--color-${twName}-${shade}: ${override ? override : `var(--color-${semantic}-${semantic === "foreground" && darkMode === true ? getInvertedStep(step) : step})`};`);
244
+ if (override) lines.push(`--color-${twName}-${shade}: ${override};`);
245
+ else {
246
+ const percent = Math.max(10, Math.min(Math.round(shade / 10), 100));
247
+ lines.push(`--color-${twName}-${shade}: color-mix(in oklch, var(--color-${semantic}) ${percent}%, transparent);`);
248
+ }
311
249
  }
312
250
  lines.push("--color-white: var(--color-background);");
313
251
  lines.push("--color-black: var(--color-foreground);");
@@ -327,7 +265,7 @@ function camelToKebab(str) {
327
265
  return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
328
266
  }
329
267
  /**
330
- * Emit --color-{name}, --color-{name}-foreground, --color-{name}-{shade} vars.
268
+ * Emit --color-{name} and --color-{name}-foreground vars.
331
269
  * Uses --color- prefix to match portal-widgets/tailwind.config.ts.
332
270
  */
333
271
  function emitColorVars(colors) {
@@ -336,7 +274,6 @@ function emitColorVars(colors) {
336
274
  const color = colors[name];
337
275
  lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
338
276
  lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
339
- for (const step of SHADE_STEPS) lines.push(`--color-${name}-${step}: ${colorToCSS(color.shades[step])};`);
340
277
  }
341
278
  return lines;
342
279
  }
@@ -380,7 +317,7 @@ const globalCSSOverride = [
380
317
  "--sidebar-primary: var(--color-primary);",
381
318
  "--sidebar-foreground: var(--color-muted-foreground);",
382
319
  "--sidebar: var(--color-muted);",
383
- "--border: var(--color-background-600);",
320
+ "--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);",
384
321
  "--ring: var(--color-primary);",
385
322
  "--popover: var(--color-background);",
386
323
  "--popover-foreground: var(--color-foreground);",
@@ -400,7 +337,7 @@ const globalCSSOverride = [
400
337
  /**
401
338
  * Overrides for global tailwindcss for specifically dark mode.
402
339
  */
403
- const globalDarkCSSOverride = ["--border: var(--color-background-400);"];
340
+ const globalDarkCSSOverride = ["--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);"];
404
341
  /**
405
342
  * Generate a complete CSS string for a resolved theme.
406
343
  * Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
@@ -419,14 +356,14 @@ function generateThemeCSS(theme, options = {}) {
419
356
  blocks.push(`${sel}[data-theme-mode="dark"] {`);
420
357
  blocks.push(...globalDarkCSSOverride);
421
358
  blocks.push(...emitColorVars(theme.dark));
422
- if (tw) blocks.push(...emitTailwindOverrides(true));
359
+ if (tw) blocks.push(...emitTailwindOverrides());
423
360
  blocks.push(`}`);
424
361
  if (!options.disableAutoTheme) {
425
362
  blocks.push(`@media (prefers-color-scheme: dark) {`);
426
363
  blocks.push(`${sel}:not([data-theme-mode]) {`);
427
364
  blocks.push(...globalDarkCSSOverride);
428
365
  blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
429
- if (tw) blocks.push(...emitTailwindOverrides(true).map((l) => `${l}`));
366
+ if (tw) blocks.push(...emitTailwindOverrides().map((l) => `${l}`));
430
367
  blocks.push(`}`);
431
368
  blocks.push(`}`);
432
369
  }
@@ -846,6 +783,6 @@ function removeAllThemes() {
846
783
  removeAllFontLinks();
847
784
  }
848
785
  //#endregion
849
- export { CountriesApiProvider as A, parseColor as C, RADIUS_KEYS as D, FONT_SIZE_KEYS as E, SEMANTIC_COLOR_NAMES as O, mergeDarkOverrides as S, FONT_FAMILY_KEYS as T, generateThemeCSS as _, getActiveThemeId as a, getContrastingTextColor as b, serialiseTheme as c, DEFAULT_FONT_SIZES as d, DEFAULT_RADII as f, getDefaultThemeDefinition as g, DEFAULT_THEME_NAME as h, buildThemeDefinition as i, useCountriesApi as j, SHADE_STEPS as k, DEFAULT_COLORS as l, DEFAULT_THEME_ID as m, removeAllThemes as n, transformThemes as o, DEFAULT_SPACING as p, removeTheme as r, deserialiseTheme as s, applyTheme as t, DEFAULT_FONT_FAMILIES as u, deriveDarkVariant as v, resolveTheme as w, getForegroundColor as x, generateShades as y };
786
+ export { resolveTheme as C, SEMANTIC_COLOR_NAMES as D, RADIUS_KEYS as E, CountriesApiProvider as O, parseColor as S, FONT_SIZE_KEYS as T, generateThemeCSS as _, getActiveThemeId as a, getForegroundColor as b, serialiseTheme as c, DEFAULT_FONT_SIZES as d, DEFAULT_RADII as f, getDefaultThemeDefinition as g, DEFAULT_THEME_NAME as h, buildThemeDefinition as i, useCountriesApi as k, DEFAULT_COLORS as l, DEFAULT_THEME_ID as m, removeAllThemes as n, transformThemes as o, DEFAULT_SPACING as p, removeTheme as r, deserialiseTheme as s, applyTheme as t, DEFAULT_FONT_FAMILIES as u, deriveDarkVariant as v, FONT_FAMILY_KEYS as w, mergeDarkOverrides as x, getContrastingTextColor as y };
850
787
 
851
- //# sourceMappingURL=src-BRTXunU1.mjs.map
788
+ //# sourceMappingURL=src-BjCPR0aG.mjs.map