@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.
- package/dist/{ContactsScreen-DflUjayA.cjs → ContactsScreen-BQ6pvYOa.cjs} +2 -2
- package/dist/{ContactsScreen-DflUjayA.cjs.map → ContactsScreen-BQ6pvYOa.cjs.map} +1 -1
- package/dist/{ContactsScreen-BOMxeTG8.cjs → ContactsScreen-Bw2GcYtk.cjs} +2 -2
- package/dist/{ContactsScreen-Bm9SlMY3.mjs → ContactsScreen-BzRFTCBS.mjs} +2 -2
- package/dist/{ContactsScreen-Bm9SlMY3.mjs.map → ContactsScreen-BzRFTCBS.mjs.map} +1 -1
- package/dist/{FluidProvider-ByBDIQeW.cjs → FluidProvider-BRkRo8Wl.cjs} +8 -8
- package/dist/{FluidProvider-ByBDIQeW.cjs.map → FluidProvider-BRkRo8Wl.cjs.map} +1 -1
- package/dist/{FluidProvider-DFZiXiqm.mjs → FluidProvider-BTZAiT69.mjs} +8 -8
- package/dist/{FluidProvider-DFZiXiqm.mjs.map → FluidProvider-BTZAiT69.mjs.map} +1 -1
- package/dist/{MessagingScreen-D6d83q2n.mjs → MessagingScreen-Bk3Eh1dN.mjs} +2 -2
- package/dist/{MessagingScreen-D6d83q2n.mjs.map → MessagingScreen-Bk3Eh1dN.mjs.map} +1 -1
- package/dist/{MessagingScreen-HT_HSiNW.cjs → MessagingScreen-DN2eQRxF.cjs} +6 -6
- package/dist/{MessagingScreen-shEWzTmQ.cjs → MessagingScreen-DtDbS3VZ.cjs} +2 -2
- package/dist/{MessagingScreen-shEWzTmQ.cjs.map → MessagingScreen-DtDbS3VZ.cjs.map} +1 -1
- package/dist/{PortalContentApiProvider-ChmcXmbv.cjs → PortalContentApiProvider-BDbrZCyI.cjs} +3 -3
- package/dist/{PortalContentApiProvider-ChmcXmbv.cjs.map → PortalContentApiProvider-BDbrZCyI.cjs.map} +1 -1
- package/dist/{PortalContentApiProvider-CNYq1_OD.mjs → PortalContentApiProvider-CzLqEN5C.mjs} +3 -3
- package/dist/{PortalContentApiProvider-CNYq1_OD.mjs.map → PortalContentApiProvider-CzLqEN5C.mjs.map} +1 -1
- package/dist/{ProductsScreen-CzfDX0xx.cjs → ProductsScreen-68jB202M.cjs} +2 -2
- package/dist/{ProductsScreen-CzfDX0xx.cjs.map → ProductsScreen-68jB202M.cjs.map} +1 -1
- package/dist/{ProductsScreen-D6-ehQjh.mjs → ProductsScreen-BeNUsjh1.mjs} +2 -2
- package/dist/{ProductsScreen-CDjpHF49.mjs → ProductsScreen-BidL3ZF5.mjs} +2 -2
- package/dist/{ProductsScreen-CDjpHF49.mjs.map → ProductsScreen-BidL3ZF5.mjs.map} +1 -1
- package/dist/{ProductsScreen-fN3fh3PB.cjs → ProductsScreen-Bq0f4pQL.cjs} +2 -2
- package/dist/{ProfileScreen-Dqu2nK_2.cjs → ProfileScreen-BMNq0NEB.cjs} +6 -6
- package/dist/{ProfileScreen-BIs70k9W.mjs → ProfileScreen-D-pTegtY.mjs} +3 -3
- package/dist/{ProfileScreen-BIs70k9W.mjs.map → ProfileScreen-D-pTegtY.mjs.map} +1 -1
- package/dist/{ProfileScreen-B0EU-TLa.cjs → ProfileScreen-D5OxmzhM.cjs} +3 -3
- package/dist/{ProfileScreen-B0EU-TLa.cjs.map → ProfileScreen-D5OxmzhM.cjs.map} +1 -1
- package/dist/{QuickShareWidget-0GD4KWAr.cjs → QuickShareWidget-C_p3tPs5.cjs} +2 -2
- package/dist/QuickShareWidget-C_p3tPs5.cjs.map +1 -0
- package/dist/{QuickShareWidget-DZzrQjOx.mjs → QuickShareWidget-xKcV3ZQ5.mjs} +2 -2
- package/dist/QuickShareWidget-xKcV3ZQ5.mjs.map +1 -0
- package/dist/{ShareablesScreen-B8rPq-_7.mjs → ShareablesScreen-BRfgOnpL.mjs} +2 -2
- package/dist/{ShareablesScreen-B8rPq-_7.mjs.map → ShareablesScreen-BRfgOnpL.mjs.map} +1 -1
- package/dist/{ShareablesScreen-DFLAJxjs.cjs → ShareablesScreen-BYP65ZnU.cjs} +2 -2
- package/dist/{ShareablesScreen-DLXK1PAg.cjs → ShareablesScreen-CCqADUXE.cjs} +2 -2
- package/dist/{ShareablesScreen-DLXK1PAg.cjs.map → ShareablesScreen-CCqADUXE.cjs.map} +1 -1
- package/dist/{ShareablesScreen-Dc57L9m8.mjs → ShareablesScreen-YnNF0dD6.mjs} +2 -2
- package/dist/{ShopScreen-2yMsyFwk.mjs → ShopScreen-BOJGcSyG.mjs} +3 -3
- package/dist/{ShopScreen-2yMsyFwk.mjs.map → ShopScreen-BOJGcSyG.mjs.map} +1 -1
- package/dist/{ShopScreen-kk4yLzrW.cjs → ShopScreen-BzyBZ24D.cjs} +6 -6
- package/dist/{ShopScreen-BmHSLZ7H.cjs → ShopScreen-DeLp93hN.cjs} +3 -3
- package/dist/{ShopScreen-BmHSLZ7H.cjs.map → ShopScreen-DeLp93hN.cjs.map} +1 -1
- package/dist/{SpacerWidget-Da_sNa_X.mjs → SpacerWidget-BJFO-Xyh.mjs} +2 -2
- package/dist/SpacerWidget-BJFO-Xyh.mjs.map +1 -0
- package/dist/{SpacerWidget-CLFbkgoz.cjs → SpacerWidget-D9lOLPr5.cjs} +2 -2
- package/dist/SpacerWidget-D9lOLPr5.cjs.map +1 -0
- package/dist/{TableWidget-lKjTu7Go.cjs → TableWidget-C7qiWZc3.cjs} +1 -1
- package/dist/{TableWidget-B65hwjKS.mjs → TableWidget-DRByd9ig.mjs} +9 -9
- package/dist/TableWidget-DRByd9ig.mjs.map +1 -0
- package/dist/{TableWidget-FDbnEYZb.cjs → TableWidget-DUnz9hrD.cjs} +9 -9
- package/dist/TableWidget-DUnz9hrD.cjs.map +1 -0
- package/dist/index.cjs +24 -26
- package/dist/index.d.cts +2 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +2 -13
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +25 -25
- package/dist/{src-DvVPCD01.cjs → src-BNcNh8fM.cjs} +18 -93
- package/dist/src-BNcNh8fM.cjs.map +1 -0
- package/dist/{src-BRTXunU1.mjs → src-BjCPR0aG.mjs} +19 -82
- package/dist/src-BjCPR0aG.mjs.map +1 -0
- package/package.json +7 -7
- package/dist/QuickShareWidget-0GD4KWAr.cjs.map +0 -1
- package/dist/QuickShareWidget-DZzrQjOx.mjs.map +0 -1
- package/dist/SpacerWidget-CLFbkgoz.cjs.map +0 -1
- package/dist/SpacerWidget-Da_sNa_X.mjs.map +0 -1
- package/dist/TableWidget-B65hwjKS.mjs.map +0 -1
- package/dist/TableWidget-FDbnEYZb.cjs.map +0 -1
- package/dist/src-BRTXunU1.mjs.map +0 -1
- 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-
|
|
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
|
-
*
|
|
275
|
-
*
|
|
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
|
|
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
|
|
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}
|
|
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-
|
|
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-
|
|
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(
|
|
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(
|
|
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-
|
|
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":";;;;;AAGA,MAAM,uBAAA,GAAA,MAAA,eAAyD,KAAK;AAEpE,MAAa,uBACX,oBAAoB;AAEtB,SAAgB,kBAAgC;CAC9C,MAAM,OAAA,GAAA,MAAA,YAAiB,oBAAoB;AAC3C,KAAI,CAAC,IACH,OAAM,IAAI,MACR,6DACD;AAEH,QAAO;;;;ACZT,MAAa,uBAAuB;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAGD,MAAa,iBAAiB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACD;AAGD,MAAa,mBAAmB,CAAC,UAAU,OAAO;AAGlD,MAAa,cAAc;CAAC;CAAS;CAAU;CAAS;CAAa;;;ACjBrE,MAAM,cAAc;;;;;;;;;AAUpB,SAAgB,WAAW,OAAsB;AAC/C,KAAI,YAAY,KAAK,MAAM,CACzB,SAAQ,IAAI;AAEd,KAAI;AACF,SAAO,IAAIA,WAAAA,QAAM,MAAM;UAChB,OAAO;AACd,UAAQ,KAAK,kCAAkC,OAAO,MAAM;AAC5D,SAAO,IAAIA,WAAAA,QAAM,SAAS;GAAC;GAAK;GAAG;GAAE,CAAC;;;;;;;;;;;AAY1C,SAAgB,mBAAmB,YAAmB,OAAqB;AACzE,KAAI,WAAW,MAAM,KAAK,QAAQ,MAAM,MAAM,KAAK,KACjD,QAAO;CAET,MAAM,WAAW,MAAM,aAAa,WAAW;AAE/C,KAAI,KAAK,IAAI,SAAS,GAAG,GACvB,QAAO,IAAIA,WAAAA,QAAM,SAAS;EACxB,MAAM,MAAM,IAAI,KAAM,MAAO;EAC7B,WAAW,MAAM,KAAK;EACtB,WAAW,MAAM,KAAK;EACvB,CAAC;AAEJ,QAAO;;;;;;;;;AAUT,SAAgB,wBAAwB,YAAmC;CACzE,MAAM,aAAa,YAAY,KAAK,WAAW,GAC3C,IAAI,eACJ;CACJ,IAAI;AACJ,KAAI;AACF,OAAK,IAAIA,WAAAA,QAAM,WAAW;SACpB;AACN,SAAO;;AAGT,QAAO,mBADW,IAAIA,WAAAA,QAAM,SAAS;EAAC;EAAM;EAAG;EAAE,CAAC,EACb,GAAG,CAAC,SAAS,EAAE,QAAQ,SAAS,CAAC;;AAUxE,MAAM,yBAOF;CACF,YAAY;EAAE,eAAe;EAAM,aAAa;EAAM;CACtD,YAAY;EAAE,eAAe;EAAM,aAAa;EAAM;CACtD,OAAO;EAAE,eAAe;EAAM,aAAa;EAAM;CACjD,SAAS;EAAE,eAAe;EAAU,aAAa;EAAM,aAAa;EAAK;CACzE,WAAW;EAAE,eAAe;EAAU,aAAa;EAAM,aAAa;EAAM;CAC5E,QAAQ;EAAE,eAAe;EAAU,aAAa;EAAM,aAAa;EAAK;CACxE,aAAa;EACX,eAAe;EACf,aAAa;EACb,aAAa;EACd;CACF;;AAGD,SAAS,gBAAgB,GAAmB;CAC1C,MAAM,WAAW,IAAI;AACrB,QAAO,KAAK,IAAI,KAAM,KAAK,IAAI,KAAM,SAAS,CAAC;;;;;AAMjD,SAAgB,kBACd,MACA,OACiB;CACjB,MAAM,SAAS,uBAAuB;CACtC,MAAM,cAAc,OAAO,eAAe;CAE1C,MAAM,gBACJ,OAAO,kBAAkB,WACrB,gBAAgB,MAAM,KAAK,MAAM,KAAK,EAAE,GACxC,OAAO;CAEb,MAAM,cACJ,OAAO,gBAAgB,WACnB,gBAAgB,MAAM,WAAW,MAAM,KAAK,EAAE,GAC9C,OAAO;AAEb,QAAO;EACL,MAAM,IAAIA,WAAAA,QAAM,SAAS;GACvB;IACC,MAAM,KAAK,MAAM,KAAK,KAAK;GAC5B,MAAM,KAAK,MAAM,KAAK;GACvB,CAAC;EACF,YAAY,IAAIA,WAAAA,QAAM,SAAS;GAC7B;IACC,MAAM,WAAW,MAAM,KAAK,KAAK;GAClC,MAAM,WAAW,MAAM,KAAK;GAC7B,CAAC;EACH;;;;;;;AAUH,SAAgB,mBACd,KAC4C;CAC5C,MAAM,aAAa,EAAE;AAErB,MAAK,MAAM,QAAQ,sBAAsB;EACvC,MAAM,aAAa,IAAI,MAAM;EAC7B,MAAM,eAAe,IAAI,KAAK;AAE9B,MAAI,cAAc,QAAQ,cAAc,WACtC,YAAW,QAAQ;WACV,cAAc;GACvB,MAAM,OACJ,aAAa,QAAQ,kBAAkB,MAAM,WAAW,CAAC;AAC3D,cAAW,QAAQ;IACX;IACN,YACE,aAAa,cACb,mBAAmB,IAAI,MAAM,WAAW,MAAM,KAAK;IACtD;QAED,YAAW,QAAQ,kBAAkB,MAAM,WAAW;;AAI1D,QAAO;;AAKT,SAAS,gBACP,QACkB;CAClB,MAAM,WAAW,EAAE;AAEnB,MAAK,MAAM,QAAQ,sBAAsB;EACvC,MAAM,QAAQ,OAAO;AACrB,WAAS,QAAQ;GACf,MAAM,MAAM,KAAK,OAAO;GACxB,YAAY,MAAM,WAAW,OAAO;GACrC;;AAGH,QAAO;;;;;;AAOT,SAAgB,aAAa,KAAqC;AAChE,QAAO;EACL,IAAI,IAAI;EACR,MAAM,IAAI;EACV,OAAO,gBAAgB,IAAI,MAAM;EACjC,MAAM,gBAAgB,mBAAmB,IAAI,CAAC;EAC9C,WAAW,EAAE,GAAG,IAAI,WAAW;EAC/B,cAAc,EAAE,GAAG,IAAI,cAAc;EACrC,SAAS,IAAI;EACb,OAAO,EAAE,GAAG,IAAI,OAAO;EACxB;;;;AClNH,MAAM,YAA6C;CACjD,mBAAmB;CACnB,oBACE;CACF,oBAAoB;CACrB;;;;;;;AAQD,SAAgB,wBAAkC;CAChD,MAAM,qBAAwD;EAC5D,MAAM;EACN,KAAK;EACL,MAAM;EACN,OAAO;EACR;CAED,MAAM,kBAAkB;EACtB;EAAI;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAAK;EAClD;CAED,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,QAAQ,aAAa,OAAO,QAAQ,mBAAmB,CACjE,MAAK,MAAM,SAAS,iBAAiB;EACnC,MAAM,WAAW,UAAU,WAAW,OAAO,GAAG;AAChD,MAAI,SACF,OAAM,KAAK,WAAW,OAAO,GAAG,MAAM,IAAI,SAAS,GAAG;OACjD;GACL,MAAM,UAAU,KAAK,IAAI,IAAI,KAAK,IAAI,KAAK,MAAM,QAAQ,GAAG,EAAE,IAAI,CAAC;AACnE,SAAM,KACJ,WAAW,OAAO,GAAG,MAAM,oCAAoC,SAAS,IAAI,QAAQ,kBACrF;;;AAKP,OAAM,KAAK,0CAA0C;AACrD,OAAM,KAAK,0CAA0C;AAErD,QAAO;;;;ACnCT,SAAS,WAAW,OAA6C;CAC/D,MAAM,SAAS,MAAM,SAAS,EAAE,QAAQ,SAAS,CAAC;AAClD,KAAI,OAAO,SAAS,MAAM,EAAE;AAC1B,UAAQ,KACN,4DACA,OACD;AACD,SAAO;;AAET,QAAO;;AAGT,SAAS,aAAa,KAAqB;AACzC,QAAO,IAAI,QAAQ,mBAAmB,QAAQ,CAAC,aAAa;;;;;;AAO9D,SAAS,cAAc,QAAoC;CACzD,MAAM,QAAkB,EAAE;AAE1B,MAAK,MAAM,QAAQ,sBAAsB;EACvC,MAAM,QAAQ,OAAO;AACrB,QAAM,KAAK,WAAW,KAAK,IAAI,WAAW,MAAM,KAAK,CAAC,GAAG;AACzD,QAAM,KAAK,WAAW,KAAK,eAAe,WAAW,MAAM,WAAW,CAAC,GAAG;;AAG5E,QAAO;;;;;;;;AAST,SAAS,iBAAiB,OAAuB;AAC/C,KAAI,MAAM,WAAW,OAAO,CAAE,QAAO;AACrC,KAAI,MAAM,SAAS,IAAI,CAAE,QAAO;AAChC,QAAO,IAAI,MAAM;;;;;AAMnB,SAAS,iBAAiB,OAAgC;CACxD,MAAM,QAAkB,EAAE;AAC1B,MAAK,MAAM,OAAO,eAChB,OAAM,KAAK,eAAe,aAAa,IAAI,CAAC,IAAI,MAAM,UAAU,KAAK,GAAG;AAE1E,MAAK,MAAM,OAAO,iBAChB,OAAM,KAAK,UAAU,IAAI,IAAI,iBAAiB,MAAM,aAAa,KAAK,CAAC,GAAG;AAE5E,OAAM,KAAK,cAAc,MAAM,QAAQ,GAAG;AAC1C,MAAK,MAAM,OAAO,YAChB,OAAM,KAAK,YAAY,aAAa,IAAI,CAAC,IAAI,MAAM,MAAM,KAAK,GAAG;AAEnE,QAAO;;;;;;AAOT,MAAM,oBAAoB;CACxB;CACA;CACA;CACA,GAAG,qBAAqB,KAAK,UAAU,KAAK,MAAM,gBAAgB,MAAM,IAAI;CAC5E,GAAG,qBAAqB,KACrB,UAAU,KAAK,MAAM,2BAA2B,MAAM,eACxD;CAED;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,MAAM,wBAAwB,CAC5B,uFACD;;;;;;AAcD,SAAgB,iBACd,OACA,UAAmC,EAAE,EAC7B;CACR,MAAM,MAAM,gBAAgB,MAAM,GAAG;CACrC,MAAM,KAAK,QAAQ,qBAAqB;CACxC,MAAM,SAAmB,EAAE;AAG3B,QAAO,KAAK,GAAG,IAAI,IAAI;AACvB,QAAO,KAAK,GAAG,kBAAkB;AACjC,QAAO,KAAK,GAAG,iBAAiB,MAAM,CAAC;AACvC,QAAO,KAAK,GAAG,cAAc,MAAM,MAAM,CAAC;AAC1C,KAAI,GAAI,QAAO,KAAK,GAAG,uBAAuB,CAAC;AAC/C,QAAO,KAAK,IAAI;AAGhB,QAAO,KAAK,GAAG,IAAI,4BAA4B;AAC/C,QAAO,KAAK,GAAG,sBAAsB;AACrC,QAAO,KAAK,GAAG,cAAc,MAAM,KAAK,CAAC;AACzC,KAAI,GAAI,QAAO,KAAK,GAAG,uBAAuB,CAAC;AAC/C,QAAO,KAAK,IAAI;AAGhB,KAAI,CAAC,QAAQ,kBAAkB;AAC7B,SAAO,KAAK,wCAAwC;AACpD,SAAO,KAAK,GAAG,IAAI,2BAA2B;AAC9C,SAAO,KAAK,GAAG,sBAAsB;AACrC,SAAO,KAAK,GAAG,cAAc,MAAM,KAAK,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC;AAC5D,MAAI,GAAI,QAAO,KAAK,GAAG,uBAAuB,CAAC,KAAK,MAAM,GAAG,IAAI,CAAC;AAClE,SAAO,KAAK,IAAI;AAChB,SAAO,KAAK,IAAI;;AAGlB,QAAO,OAAO,KAAK,KAAK;;;;AC1J1B,MAAa,qBAAkD;CAC7D,YAAY;CACZ,OAAO;CACP,SAAS;CACT,OAAO;CACP,YAAY;CACZ,OAAO;CACR;AAED,MAAa,wBAAuD;CAClE,QAAQ;CACR,MAAM;CACP;AAED,MAAa,kBAAkB;AAE/B,MAAa,gBAA2C;CACtD,OAAO;CACP,QAAQ;CACR,OAAO;CACP,YAAY;CACb;AAID,MAAa,iBAAiB;CAC5B,YAAY;CACZ,YAAY;CACZ,SAAS;CACT,WAAW;CACX,QAAQ;CACR,OAAO;CACP,aAAa;CACb,iBAAiB;CAClB;AAID,MAAa,mBAAmB;AAChC,MAAa,qBAAqB;;;;;AAQlC,SAAgB,4BAA6C;CAC3D,MAAM,KAAK,IAAIC,WAAAA,QAAM,eAAe,WAAW;CAC/C,MAAM,KAAK,IAAIA,WAAAA,QAAM,eAAe,WAAW;CAC/C,MAAM,UAAU,IAAIA,WAAAA,QAAM,eAAe,QAAQ;CACjD,MAAM,YAAY,IAAIA,WAAAA,QAAM,eAAe,UAAU;CACrD,MAAM,SAAS,IAAIA,WAAAA,QAAM,eAAe,OAAO;CAC/C,MAAM,QAAQ,IAAIA,WAAAA,QAAM,eAAe,MAAM;CAC7C,MAAM,cAAc,IAAIA,WAAAA,QAAM,eAAe,YAAY;CACzD,MAAM,UAAU,IAAIA,WAAAA,QAAM,eAAe,gBAAgB;CAEzD,MAAM,SAAS,IAAIA,WAAAA,QAAM,UAAU;CACnC,MAAM,SAAS,IAAIA,WAAAA,QAAM,UAAU;CACnC,MAAM,YAAY,IAAIA,WAAAA,QAAM,UAAU;CACtC,MAAM,sBAAsB,IAAIA,WAAAA,QAAM,UAAU;AAEhD,QAAO;EACL,IAAI;EACJ,MAAM;EACN,OAAO;GACL,YAAY;IAAE,MAAM;IAAI,YAAY;IAAI;GACxC,YAAY;IAAE,MAAM;IAAI,YAAY;IAAI;GACxC,SAAS;IACP,MAAM;IACN,YAAY,mBAAmB,IAAI,QAAQ;IAC5C;GACD,WAAW;IACT,MAAM;IACN,YAAY,mBAAmB,IAAI,UAAU;IAC9C;GACD,QAAQ;IACN,MAAM;IACN,YAAY,mBAAmB,IAAI,OAAO;IAC3C;GACD,OAAO;IAAE,MAAM;IAAO,YAAY;IAAS;GAC3C,aAAa;IACX,MAAM;IACN,YAAY,mBAAmB,IAAI,YAAY;IAChD;GACF;EACD,MAAM;GACJ,YAAY;IAAE,MAAM;IAAQ,YAAY;IAAQ;GAChD,YAAY;IAAE,MAAM;IAAQ,YAAY;IAAQ;GAChD,OAAO;IAAE,MAAM;IAAW,YAAY;IAAqB;GAC5D;EACD,WAAW,EAAE,GAAG,oBAAoB;EACpC,cAAc,EAAE,GAAG,uBAAuB;EAC1C,SAAS;EACT,OAAO,EAAE,GAAG,eAAe;EAC5B;;;;ACtFH,SAAS,aAAa,OAA0B;AAC9C,QAAO;EACL,GAAG,MAAM,MAAM,KAAK;EACpB,GAAG,MAAM,MAAM,KAAK;EACpB,GAAG,MAAM,MAAM,KAAK;EACrB;;AAGH,SAAS,aAAa,OAA0B;AAC9C,QAAO,IAAIC,WAAAA,QAAM,SAAS;EAAC,MAAM;EAAG,MAAM;EAAG,MAAM;EAAE,CAAC;;;;;;AAOxD,SAAgB,eAAe,KAAoC;CACjE,MAAM,QAAQ,EAAE;AAChB,MAAK,MAAM,QAAQ,qBACjB,OAAM,QAAQ;EACZ,MAAM,aAAa,IAAI,MAAM,MAAM,KAAK;EACxC,YAAY,aAAa,IAAI,MAAM,MAAM,WAAW;EACrD;CAGH,MAAM,OAA6B,EAAE;AACrC,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,IAAI,KAAK,EAAE;AACpD,MAAI,CAAC,MAAO;AACZ,OAAK,QAA6B;GAChC,GAAI,MAAM,OAAO,EAAE,MAAM,aAAa,MAAM,KAAK,EAAE,GAAG,EAAE;GACxD,GAAI,MAAM,aACN,EAAE,YAAY,aAAa,MAAM,WAAW,EAAE,GAC9C,EAAE;GACP;;AAGH,QAAO;EACL,IAAI,IAAI;EACR,MAAM,IAAI;EACV;EACA;EACA,WAAW,EAAE,GAAG,IAAI,WAAW;EAC/B,cAAc,EAAE,GAAG,IAAI,cAAc;EACrC,SAAS,IAAI;EACb,OAAO,EAAE,GAAG,IAAI,OAAO;EACvB,GAAI,IAAI,sBAAsB,EAAE,qBAAqB,MAAM,GAAG,EAAE;EACjE;;;;;;;AAQH,SAAgB,iBACd,SACiB;CACjB,MAAM,WAAa,QAAQ,SACzB,EAAE;CACJ,MAAM,UAAY,QAAQ,QACxB,EAAE;CAEJ,MAAM,WAAW,2BAA2B;CAC5C,MAAM,QAAQ,EAAE;AAChB,MAAK,MAAM,QAAQ,sBAAsB;EACvC,MAAM,QAAQ,SAAS;AACvB,MAAI,MACF,OAAM,QAAQ;GACZ,MAAM,aAAa,MAAM,KAAK;GAC9B,YAAY,aAAa,MAAM,WAAW;GAC3C;OACI;AACL,WAAQ,KACN,kDAAkD,KAAK,kBACxD;AACD,SAAM,QAAQ,SAAS,MAAM;;;CAIjC,MAAM,OAAqE,EAAE;AAC7E,MAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,EAAE;AACnD,MAAI,CAAC,MAAO;AACZ,OAAK,QAA6B;GAChC,GAAI,MAAM,OAAO,EAAE,MAAM,aAAa,MAAM,KAAK,EAAE,GAAG,EAAE;GACxD,GAAI,MAAM,aACN,EAAE,YAAY,aAAa,MAAM,WAAW,EAAE,GAC9C,EAAE;GACP;;AAGH,QAAO;EACL,IAAI,QAAQ;EACZ,MAAM,QAAQ;EACd;EACA;EACA,WAAY,QAAQ,aAAa;EAIjC,cAAe,QAAQ,gBAAgB;EAIvC,SAAU,QAAQ,WAAA;EAClB,OAAQ,QAAQ,SAAS;EACzB,GAAI,QAAQ,wBAAwB,OAChC,EAAE,qBAAqB,MAAM,GAC7B,EAAE;EACP;;;;;;;;ACnGH,SAAS,iBAAiB,QAA0C;AAClE,QAAO,OAAO,SAAS,QAAQ,OAAO,OAAO,UAAU;;;;;;AAOzD,SAAS,yBACP,IACA,MACA,QACiB;CACjB,MAAM,KAAK,WACT,OAAO,QAAQ,OAAO,cAAc,eAAe,WACpD;CACD,MAAM,KAAK,WACT,OAAO,QAAQ,OAAO,cAAc,eAAe,WACpD;CACD,MAAM,UAAU,WAAW,OAAO,WAAW,eAAe,QAAQ;CACpE,MAAM,YAAY,WAAW,OAAO,aAAa,eAAe,UAAU;CAC1E,MAAM,SAAS,WAAW,OAAO,UAAU,eAAe,OAAO;CACjE,MAAM,QAAQ,WAAW,OAAO,SAAS,eAAe,MAAM;CAC9D,MAAM,cAAc,WAClB,OAAO,eAAe,eAAe,YACtC;CACD,MAAM,UAAU,WACd,OAAO,mBAAmB,eAAe,gBAC1C;AAED,QAAO;EACL,IAAI,OAAO,GAAG;EACd;EACA,OAAO;GACL,YAAY;IAAE,MAAM;IAAI,YAAY;IAAI;GACxC,YAAY;IAAE,MAAM;IAAI,YAAY;IAAI;GACxC,SAAS;IACP,MAAM;IACN,YAAY,mBAAmB,IAAI,QAAQ;IAC5C;GACD,WAAW;IACT,MAAM;IACN,YAAY,mBAAmB,IAAI,UAAU;IAC9C;GACD,QAAQ;IACN,MAAM;IACN,YAAY,mBAAmB,IAAI,OAAO;IAC3C;GACD,OAAO;IAAE,MAAM;IAAO,YAAY;IAAS;GAC3C,aAAa;IACX,MAAM;IACN,YAAY,mBAAmB,IAAI,YAAY;IAChD;GACF;EACD,MAAM,EAAE;EACR,WAAW;GACT,YAAY,OAAO,cAAc,mBAAmB;GACpD,OAAO,OAAO,SAAS,mBAAmB;GAC1C,SAAS,OAAO,WAAW,mBAAmB;GAC9C,OAAO,OAAO,SAAS,mBAAmB;GAC1C,YAAY,OAAO,cAAc,mBAAmB;GACpD,OAAO,OAAO,SAAS,mBAAmB;GAC3C;EACD,cAAc;GACZ,QAAQ,OAAO,cAAc,sBAAsB;GACnD,MAAM,OAAO,YAAY,sBAAsB;GAChD;EACD,SAAS,OAAO,iBAAA;EAChB,OAAO;GACL,OAAO,OAAO,eAAe,cAAc;GAC3C,QAAQ,OAAO,gBAAgB,cAAc;GAC7C,OAAO,OAAO,eAAe,cAAc;GAC3C,YAAY,OAAO,oBAAoB,cAAc;GACtD;EACF;;;;;;AAOH,SAAgB,qBAAqB,OAAqC;CACxE,MAAM,SAAU,MAAM,UAAU,EAAE;AAElC,KAAI,iBAAiB,OAAO,CAC1B,QAAO,iBAAiB;EACtB,GAAG;EACH,IAAI,OAAO,MAAM,GAAG;EACpB,MAAM,MAAM,QAAQ;EACrB,CAAC;AAGJ,QAAO,yBACL,MAAM,IACN,MAAM,QAAQ,kBACd,OACD;;;;;;AAOH,SAAgB,gBAAgB,QAA0C;AACxE,QAAO,OAAO,SAAS,UAAU;AAC/B,MAAI;AACF,UAAO,CAAC,qBAAqB,MAAM,CAAC;WAC7B,OAAO;AACd,WAAQ,MAAM,oCAAoC,MAAM,GAAG,IAAI,MAAM;AACrE,UAAO,EAAE;;GAEX;;;;;;AAOJ,SAAgB,iBAAiB,QAA2C;CAC1E,MAAM,SAAS,OAAO,MAAM,MAAM,EAAE,OAAO,IAAI,OAAO;AACtD,QAAO,SAAS,OAAO,OAAO,GAAG,GAAG,KAAA;;;;ACjJtC,MAAM,eAAe;AACrB,MAAM,mBAAmB;AAEzB,MAAM,eAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;;AAGF,SAAS,mBAAmB,QAAwB;AAElD,QAAO,4CADS,mBAAmB,OAAO,CAAC,QAAQ,QAAQ,IAAI,CACJ;;;AAI7D,SAAS,eAAe,OAAwB;AAC9C,KAAI,CAAC,MAAO,QAAO;AACnB,KAAI,MAAM,WAAW,OAAO,CAAE,QAAO;AACrC,QAAO,CAAC,aAAa,IAAI,MAAM,aAAa,CAAC;;;AAI/C,SAAS,WAAW,QAAwB;AAC1C,QAAO,GAAG,mBAAmB,OAAO,QAAQ,QAAQ,IAAI,CAAC,aAAa;;;;;;AAOxE,SAAS,eAAe,OAA4B;AAClD,KAAI,OAAO,aAAa,YAAa;CAErC,MAAM,8BAAc,IAAI,KAAa;AACrC,MAAK,MAAM,OAAO,kBAAkB;EAClC,MAAM,QAAQ,MAAM,aAAa;AACjC,MAAI,eAAe,MAAM,CACvB,aAAY,IAAI,MAAM;;AAKJ,UAAS,iBAC7B,aAAa,iBAAiB,IAC/B,CACa,SAAS,SAAS;EAC9B,MAAM,SAAS,KAAK,aAAa,sBAAsB,EAAE,MAAM,IAAI,IAAI,EAAE;AACzE,MAAI,CAAC,OAAO,SAAS,MAAM,GAAG,CAAE;EAChC,MAAM,WAAW,KAAK,aAAa,mBAAmB;AACtD,MAAI,YAAY,CAAC,YAAY,IAAI,SAAS,EAAE;GAC1C,MAAM,YAAY,OAAO,QAAQ,OAAO,OAAO,MAAM,GAAG;AACxD,OAAI,UAAU,WAAW,EACvB,MAAK,QAAQ;OAEb,MAAK,aAAa,uBAAuB,UAAU,KAAK,IAAI,CAAC;;GAGjE;AAGF,MAAK,MAAM,UAAU,aAAa;EAChC,MAAM,KAAK,WAAW,OAAO;EAC7B,MAAM,WAAW,SAAS,eAAe,GAAG;AAC5C,MAAI,UAAU;GACZ,MAAM,SACJ,SAAS,aAAa,sBAAsB,EAAE,MAAM,IAAI,IAAI,EAAE;AAChE,OAAI,CAAC,OAAO,SAAS,MAAM,GAAG,CAC5B,UAAS,aACP,uBACA,CAAC,GAAG,QAAQ,MAAM,GAAG,CAAC,KAAK,IAAI,CAChC;SAEE;GACL,MAAM,OAAO,SAAS,cAAc,OAAO;AAC3C,QAAK,KAAK;AACV,QAAK,MAAM;AACX,QAAK,OAAO,mBAAmB,OAAO;AACtC,QAAK,aAAa,oBAAoB,OAAO;AAC7C,QAAK,aAAa,uBAAuB,MAAM,GAAG;AAClD,YAAS,KAAK,YAAY,KAAK;;;;;AAMrC,SAAS,qBAA2B;AAClC,KAAI,OAAO,aAAa,YAAa;AACrC,UACG,iBAAiB,aAAa,iBAAiB,IAAI,CACnD,SAAS,OAAO,GAAG,QAAQ,CAAC;;;;;;;;;AAUjC,SAAgB,WACd,OACA,SACM;AACN,KAAI,OAAO,aAAa,YAAa;AAErC,KAAI;AACF,iBAAe,MAAM;EAErB,MAAM,UAAU,GAAG,eAAe,MAAM;EACxC,IAAI,KAAK,SAAS,eAAe,QAAQ;AAEzC,MAAI,CAAC,IAAI;AACP,QAAK,SAAS,cAAc,QAAQ;AACpC,MAAG,KAAK;AACR,YAAS,KAAK,YAAY,GAAG;;AAG/B,KAAG,cAAc,iBAAiB,OAAO,QAAQ;UAC1C,OAAO;AACd,UAAQ,MAAM,kCAAkC,MAAM,GAAG,KAAK,MAAM;;;;AAKxE,SAAgB,YAAY,SAAuB;AACjD,KAAI,OAAO,aAAa,YAAa;AACrC,UAAS,eAAe,GAAG,eAAe,UAAU,EAAE,QAAQ;AAG9D,UACG,iBAAiB,aAAa,iBAAiB,IAAI,CACnD,SAAS,SAAS;EAEjB,MAAM,aADS,KAAK,aAAa,sBAAsB,EAAE,MAAM,IAAI,IAAI,EAAE,EAChD,QAAQ,OAAO,OAAO,QAAQ;AACvD,MAAI,UAAU,WAAW,EACvB,MAAK,QAAQ;MAEb,MAAK,aAAa,uBAAuB,UAAU,KAAK,IAAI,CAAC;GAE/D;;;AAIN,SAAgB,kBAAwB;AACtC,KAAI,OAAO,aAAa,YAAa;AACrC,UACG,iBAAiB,cAAc,aAAa,IAAI,CAChD,SAAS,OAAO,GAAG,QAAQ,CAAC;AAC/B,qBAAoB"}
|
|
@@ -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-
|
|
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
|
-
*
|
|
273
|
-
*
|
|
274
|
-
|
|
275
|
-
|
|
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(
|
|
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
|
|
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}
|
|
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-
|
|
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-
|
|
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(
|
|
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(
|
|
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 {
|
|
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-
|
|
788
|
+
//# sourceMappingURL=src-BjCPR0aG.mjs.map
|