@aglyn/shared-ui-theme 1.0.0-beta.143

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 (78) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +7 -0
  3. package/package.json +51 -0
  4. package/src/index.d.ts +34 -0
  5. package/src/index.js +50 -0
  6. package/src/index.js.map +1 -0
  7. package/src/lib/components/host-theme-provider.d.ts +84 -0
  8. package/src/lib/components/host-theme-provider.js +146 -0
  9. package/src/lib/components/host-theme-provider.js.map +1 -0
  10. package/src/lib/components/theme-css-var-provider.d.ts +44 -0
  11. package/src/lib/components/theme-css-var-provider.js +79 -0
  12. package/src/lib/components/theme-css-var-provider.js.map +1 -0
  13. package/src/lib/console.theme.d.ts +34 -0
  14. package/src/lib/console.theme.js +598 -0
  15. package/src/lib/console.theme.js.map +1 -0
  16. package/src/lib/constants.d.ts +72 -0
  17. package/src/lib/constants.js +85 -0
  18. package/src/lib/constants.js.map +1 -0
  19. package/src/lib/hocs/create-with-emotion-client-cache.d.ts +30 -0
  20. package/src/lib/hocs/create-with-emotion-client-cache.js +53 -0
  21. package/src/lib/hocs/create-with-emotion-client-cache.js.map +1 -0
  22. package/src/lib/hocs/create-with-theme-provider.d.ts +86 -0
  23. package/src/lib/hocs/create-with-theme-provider.js +231 -0
  24. package/src/lib/hocs/create-with-theme-provider.js.map +1 -0
  25. package/src/lib/tenant.theme.d.ts +58 -0
  26. package/src/lib/tenant.theme.js +282 -0
  27. package/src/lib/tenant.theme.js.map +1 -0
  28. package/src/lib/theme.types.d.ts +26 -0
  29. package/src/lib/theme.types.js +18 -0
  30. package/src/lib/theme.types.js.map +1 -0
  31. package/src/lib/util/accent-text.d.ts +187 -0
  32. package/src/lib/util/accent-text.js +277 -0
  33. package/src/lib/util/accent-text.js.map +1 -0
  34. package/src/lib/util/accessible-shade.d.ts +61 -0
  35. package/src/lib/util/accessible-shade.js +117 -0
  36. package/src/lib/util/accessible-shade.js.map +1 -0
  37. package/src/lib/util/color-scheme-hint.d.ts +68 -0
  38. package/src/lib/util/color-scheme-hint.js +69 -0
  39. package/src/lib/util/color-scheme-hint.js.map +1 -0
  40. package/src/lib/util/create-responsive-theme.d.ts +71 -0
  41. package/src/lib/util/create-responsive-theme.js +287 -0
  42. package/src/lib/util/create-responsive-theme.js.map +1 -0
  43. package/src/lib/util/emotion-cache.d.ts +81 -0
  44. package/src/lib/util/emotion-cache.js +79 -0
  45. package/src/lib/util/emotion-cache.js.map +1 -0
  46. package/src/lib/util/generate-component-class-keys.d.ts +41 -0
  47. package/src/lib/util/generate-component-class-keys.js +53 -0
  48. package/src/lib/util/generate-component-class-keys.js.map +1 -0
  49. package/src/lib/util/host-theme.d.ts +92 -0
  50. package/src/lib/util/host-theme.js +340 -0
  51. package/src/lib/util/host-theme.js.map +1 -0
  52. package/src/lib/util/layered-emotion-cache.d.ts +78 -0
  53. package/src/lib/util/layered-emotion-cache.js +90 -0
  54. package/src/lib/util/layered-emotion-cache.js.map +1 -0
  55. package/src/lib/util/merge-sx-props.d.ts +22 -0
  56. package/src/lib/util/merge-sx-props.js +27 -0
  57. package/src/lib/util/merge-sx-props.js.map +1 -0
  58. package/src/lib/util/scheme-route-segment.d.ts +95 -0
  59. package/src/lib/util/scheme-route-segment.js +101 -0
  60. package/src/lib/util/scheme-route-segment.js.map +1 -0
  61. package/src/lib/util/theme-editor-defaults.d.ts +41 -0
  62. package/src/lib/util/theme-editor-defaults.js +49 -0
  63. package/src/lib/util/theme-editor-defaults.js.map +1 -0
  64. package/src/lib/util/theme-editor-fields.d.ts +315 -0
  65. package/src/lib/util/theme-editor-fields.js +714 -0
  66. package/src/lib/util/theme-editor-fields.js.map +1 -0
  67. package/src/lib/util/theme-mode-cookie.d.ts +48 -0
  68. package/src/lib/util/theme-mode-cookie.js +48 -0
  69. package/src/lib/util/theme-mode-cookie.js.map +1 -0
  70. package/src/vendor/emotion.d.ts +18 -0
  71. package/src/vendor/emotion.js +22 -0
  72. package/src/vendor/emotion.js.map +1 -0
  73. package/src/vendor/jss.d.ts +18 -0
  74. package/src/vendor/jss.js +20 -0
  75. package/src/vendor/jss.js.map +1 -0
  76. package/src/vendor/mui.d.ts +282 -0
  77. package/src/vendor/mui.js +20 -0
  78. package/src/vendor/mui.js.map +1 -0
@@ -0,0 +1,117 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ import { decomposeColor, hslToRgb, rgbToHex } from "../../vendor/mui.js";
17
+ /** WCAG 2.x AA contrast bar for normal-size text. */ export const AA_TEXT_CONTRAST = 4.5;
18
+ function srgbChannelToLinear(channel) {
19
+ const value = channel / 255;
20
+ return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
21
+ }
22
+ /**
23
+ * Exact WCAG relative luminance. MUI's `getLuminance` truncates to three
24
+ * digits, which is enough slop to misclassify a borderline shade either side
25
+ * of the 4.5:1 bar — the whole reason this module exists — so the math here
26
+ * keeps full precision. Alpha is ignored (this pipeline derives opaque
27
+ * tokens), matching MUI's behaviour for rgba inputs.
28
+ */ export function relativeLuminance(color) {
29
+ const decomposed = decomposeColor(color);
30
+ if (decomposed.type === 'color') {
31
+ // `color(display-p3 …)` values are not 0–255 channels; a caller that
32
+ // feeds these must handle its own contrast.
33
+ throw new Error(`accessible-shade: unsupported color format "${color}"`);
34
+ }
35
+ const values = decomposed.type.startsWith('hsl') ? decomposeColor(hslToRgb(color)).values : decomposed.values;
36
+ return 0.2126 * srgbChannelToLinear(values[0]) + 0.7152 * srgbChannelToLinear(values[1]) + 0.0722 * srgbChannelToLinear(values[2]);
37
+ }
38
+ /** Exact WCAG contrast ratio between two colors, 1–21. */ export function contrastRatio(colorA, colorB) {
39
+ const luminanceA = relativeLuminance(colorA);
40
+ const luminanceB = relativeLuminance(colorB);
41
+ return (Math.max(luminanceA, luminanceB) + 0.05) / (Math.min(luminanceA, luminanceB) + 0.05);
42
+ }
43
+ /** True when `foreground` clears `minContrast` against EVERY background. */ export function meetsContrast(foreground, backgrounds, minContrast = AA_TEXT_CONTRAST) {
44
+ return backgrounds.every((background)=>contrastRatio(foreground, background) >= minContrast);
45
+ }
46
+ /** [hue 0–360, saturation 0–1, lightness 0–1] */ function toHslValues(color) {
47
+ const decomposed = decomposeColor(color);
48
+ if (decomposed.type.startsWith('hsl')) {
49
+ const [h, s, l] = decomposed.values;
50
+ return [
51
+ h,
52
+ s / 100,
53
+ l / 100
54
+ ];
55
+ }
56
+ const r = decomposed.values[0] / 255;
57
+ const g = decomposed.values[1] / 255;
58
+ const b = decomposed.values[2] / 255;
59
+ const max = Math.max(r, g, b);
60
+ const min = Math.min(r, g, b);
61
+ const lightness = (max + min) / 2;
62
+ if (max === min) return [
63
+ 0,
64
+ 0,
65
+ lightness
66
+ ];
67
+ const delta = max - min;
68
+ const saturation = lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min);
69
+ let hue;
70
+ if (max === r) hue = (g - b) / delta + (g < b ? 6 : 0);
71
+ else if (max === g) hue = (b - r) / delta + 2;
72
+ else hue = (r - g) / delta + 4;
73
+ return [
74
+ hue * 60,
75
+ saturation,
76
+ lightness
77
+ ];
78
+ }
79
+ function hslToHex(hue, saturation, lightness) {
80
+ return rgbToHex(hslToRgb(`hsl(${hue}, ${saturation * 100}%, ${lightness * 100}%)`));
81
+ }
82
+ /**
83
+ * Nearest shade of `color`, walking HSL lightness in `direction` only, that
84
+ * clears `minContrast` against every background (AGL-1297). Hue and
85
+ * saturation are held fixed so the result stays the same colour, just
86
+ * deeper or lighter.
87
+ *
88
+ * - If `color` already clears the bar, it is returned UNCHANGED —
89
+ * byte-identical, so a value that was fine yesterday stays byte-stable.
90
+ * - The walk is one-directional: pass `'darken'` for a foreground on light
91
+ * backgrounds, `'lighten'` for a foreground on dark backgrounds.
92
+ * - If the bar is unreachable in that direction (e.g. lightening against a
93
+ * mid-grey), the walk stops at the pole (black/white) or the iteration
94
+ * cap and returns that best-effort value rather than looping or
95
+ * reversing direction.
96
+ */ export function accessibleShade(color, backgrounds, direction, options = {}) {
97
+ const { minContrast = AA_TEXT_CONTRAST, step = 0.01, maxSteps = 120 } = options;
98
+ if (!backgrounds.length || meetsContrast(color, backgrounds, minContrast)) {
99
+ return color;
100
+ }
101
+ const [hue, saturation, initialLightness] = toHslValues(color);
102
+ const delta = direction === 'lighten' ? step : -step;
103
+ let lightness = initialLightness;
104
+ let candidate = color;
105
+ for(let i = 0; i < maxSteps; i += 1){
106
+ lightness = Math.min(1, Math.max(0, lightness + delta));
107
+ candidate = hslToHex(hue, saturation, lightness);
108
+ if (meetsContrast(candidate, backgrounds, minContrast)) return candidate;
109
+ // Pole reached without clearing: nothing further exists in this
110
+ // direction, so return the extreme as the best effort.
111
+ if (lightness === 0 || lightness === 1) return candidate;
112
+ }
113
+ return candidate;
114
+ }
115
+ export default accessibleShade;
116
+
117
+ //# sourceMappingURL=accessible-shade.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/accessible-shade.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { decomposeColor, hslToRgb, rgbToHex } from '../../vendor/mui'\n\n/** Ramp direction for a shade walk. */\nexport type ShadeDirection = 'darken' | 'lighten'\n\n/** WCAG 2.x AA contrast bar for normal-size text. */\nexport const AA_TEXT_CONTRAST = 4.5\n\nexport type AccessibleShadeOptions = {\n /** Contrast every background must clear. Default {@link AA_TEXT_CONTRAST}. */\n minContrast?: number\n /** HSL lightness moved per iteration (0–1 scale). Default 0.01. */\n step?: number\n /**\n * Iteration cap. The default covers the full lightness range at the\n * default step with headroom, so the walk always terminates at a pole\n * before hitting it in practice.\n */\n maxSteps?: number\n}\n\nfunction srgbChannelToLinear(channel: number): number {\n const value = channel / 255\n return value <= 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4\n}\n\n/**\n * Exact WCAG relative luminance. MUI's `getLuminance` truncates to three\n * digits, which is enough slop to misclassify a borderline shade either side\n * of the 4.5:1 bar — the whole reason this module exists — so the math here\n * keeps full precision. Alpha is ignored (this pipeline derives opaque\n * tokens), matching MUI's behaviour for rgba inputs.\n */\nexport function relativeLuminance(color: string): number {\n const decomposed = decomposeColor(color)\n if (decomposed.type === 'color') {\n // `color(display-p3 …)` values are not 0–255 channels; a caller that\n // feeds these must handle its own contrast.\n throw new Error(`accessible-shade: unsupported color format \"${color}\"`)\n }\n const values = decomposed.type.startsWith('hsl')\n ? decomposeColor(hslToRgb(color)).values\n : decomposed.values\n return (\n 0.2126 * srgbChannelToLinear(values[0]) +\n 0.7152 * srgbChannelToLinear(values[1]) +\n 0.0722 * srgbChannelToLinear(values[2])\n )\n}\n\n/** Exact WCAG contrast ratio between two colors, 1–21. */\nexport function contrastRatio(colorA: string, colorB: string): number {\n const luminanceA = relativeLuminance(colorA)\n const luminanceB = relativeLuminance(colorB)\n return (\n (Math.max(luminanceA, luminanceB) + 0.05) /\n (Math.min(luminanceA, luminanceB) + 0.05)\n )\n}\n\n/** True when `foreground` clears `minContrast` against EVERY background. */\nexport function meetsContrast(\n foreground: string,\n backgrounds: ReadonlyArray<string>,\n minContrast: number = AA_TEXT_CONTRAST,\n): boolean {\n return backgrounds.every(\n (background) => contrastRatio(foreground, background) >= minContrast,\n )\n}\n\n/** [hue 0–360, saturation 0–1, lightness 0–1] */\nfunction toHslValues(color: string): [number, number, number] {\n const decomposed = decomposeColor(color)\n if (decomposed.type.startsWith('hsl')) {\n const [h, s, l] = decomposed.values\n return [h, s / 100, l / 100]\n }\n const r = decomposed.values[0] / 255\n const g = decomposed.values[1] / 255\n const b = decomposed.values[2] / 255\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const lightness = (max + min) / 2\n if (max === min) return [0, 0, lightness]\n const delta = max - min\n const saturation =\n lightness > 0.5 ? delta / (2 - max - min) : delta / (max + min)\n let hue: number\n if (max === r) hue = (g - b) / delta + (g < b ? 6 : 0)\n else if (max === g) hue = (b - r) / delta + 2\n else hue = (r - g) / delta + 4\n return [hue * 60, saturation, lightness]\n}\n\nfunction hslToHex(hue: number, saturation: number, lightness: number): string {\n return rgbToHex(\n hslToRgb(`hsl(${hue}, ${saturation * 100}%, ${lightness * 100}%)`),\n )\n}\n\n/**\n * Nearest shade of `color`, walking HSL lightness in `direction` only, that\n * clears `minContrast` against every background (AGL-1297). Hue and\n * saturation are held fixed so the result stays the same colour, just\n * deeper or lighter.\n *\n * - If `color` already clears the bar, it is returned UNCHANGED —\n * byte-identical, so a value that was fine yesterday stays byte-stable.\n * - The walk is one-directional: pass `'darken'` for a foreground on light\n * backgrounds, `'lighten'` for a foreground on dark backgrounds.\n * - If the bar is unreachable in that direction (e.g. lightening against a\n * mid-grey), the walk stops at the pole (black/white) or the iteration\n * cap and returns that best-effort value rather than looping or\n * reversing direction.\n */\nexport function accessibleShade(\n color: string,\n backgrounds: ReadonlyArray<string>,\n direction: ShadeDirection,\n options: AccessibleShadeOptions = {},\n): string {\n const {\n minContrast = AA_TEXT_CONTRAST,\n step = 0.01,\n maxSteps = 120,\n } = options\n if (!backgrounds.length || meetsContrast(color, backgrounds, minContrast)) {\n return color\n }\n const [hue, saturation, initialLightness] = toHslValues(color)\n const delta = direction === 'lighten' ? step : -step\n let lightness = initialLightness\n let candidate = color\n for (let i = 0; i < maxSteps; i += 1) {\n lightness = Math.min(1, Math.max(0, lightness + delta))\n candidate = hslToHex(hue, saturation, lightness)\n if (meetsContrast(candidate, backgrounds, minContrast)) return candidate\n // Pole reached without clearing: nothing further exists in this\n // direction, so return the extreme as the best effort.\n if (lightness === 0 || lightness === 1) return candidate\n }\n return candidate\n}\n\nexport default accessibleShade\n"],"names":["decomposeColor","hslToRgb","rgbToHex","AA_TEXT_CONTRAST","srgbChannelToLinear","channel","value","relativeLuminance","color","decomposed","type","Error","values","startsWith","contrastRatio","colorA","colorB","luminanceA","luminanceB","Math","max","min","meetsContrast","foreground","backgrounds","minContrast","every","background","toHslValues","h","s","l","r","g","b","lightness","delta","saturation","hue","hslToHex","accessibleShade","direction","options","step","maxSteps","length","initialLightness","candidate","i"],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import type { ThemeMode } from '../hocs/create-with-theme-provider';
18
+ /**
19
+ * The device's own light/dark preference, carried on the request.
20
+ *
21
+ * `prefers-color-scheme` is a media feature, and a media feature is a browser
22
+ * fact: a server render has no viewport to evaluate it against, so
23
+ * `useMediaQuery` reports light for every visitor whose device is dark. This
24
+ * user-agent client hint is the same preference in a form the server CAN read
25
+ * — the browser puts it on the request, so a visitor following their device
26
+ * gets their scheme in the first byte rather than a light document that
27
+ * repaints once React has hydrated the whole page.
28
+ *
29
+ * A browser sends it only where the origin asked for it, and the tenant
30
+ * middleware is what asks — it advertises this same token in `Accept-CH` and
31
+ * `Critical-CH` and splits its cache on it in `Vary`, from a copy of its own,
32
+ * since an edge bundle takes no library imports. Reading the request header by
33
+ * this constant is safe whatever the transport did to the casing: `Headers`
34
+ * matches case-insensitively.
35
+ *
36
+ * ⚠️ CHROMIUM ONLY. Firefox and Safari implement neither this hint nor the
37
+ * `Accept-CH` negotiation that asks for it, so on those browsers the header is
38
+ * simply absent and the scheme settles once the page has hydrated. Absence is
39
+ * therefore an ordinary answer, never an error: every reader must have a
40
+ * behavior for "the request did not say".
41
+ *
42
+ * Nothing in this module imports React, MUI or `js-cookie`, and its one import
43
+ * is a type erased at compile time. That is what lets a Server Component take
44
+ * it without pulling the theme library's context providers into the RSC graph
45
+ * — the same reason `util/theme-mode-cookie`, `util/emotion-cache` and
46
+ * `util/host-theme` are deep-imported rather than taken from the barrel.
47
+ */
48
+ export declare const COLOR_SCHEME_HINT_HEADER = "Sec-CH-Prefers-Color-Scheme";
49
+ /**
50
+ * A `Sec-CH-Prefers-Color-Scheme` request-header value as a theme mode.
51
+ *
52
+ * Only `light` and `dark` are schemes; anything else — a header the browser
53
+ * never sent, a value from a future revision of the media feature — is `null`,
54
+ * meaning "the request named no scheme, fall back to the browser's own answer
55
+ * once there is one". A caller that mistook an unknown value for light would
56
+ * pin a dark device to the wrong scheme for the whole render.
57
+ *
58
+ * The value is a structured-field string, so it may arrive quoted (`"dark"`);
59
+ * the quotes are stripped and the token is compared case-insensitively so one
60
+ * parser reads every spelling a conforming user agent may send.
61
+ *
62
+ * This is the device-preference counterpart to `parseThemeModeCookie`, which
63
+ * reads the visitor's EXPLICIT choice off the same request. The two are
64
+ * separate answers on purpose and are never merged here: a stated preference
65
+ * outranks a device default, and the layering that enforces that lives in
66
+ * `useThemeModeState`, where the switcher can still tell them apart.
67
+ */
68
+ export declare function parseColorSchemeHint(value: string | undefined | null): ThemeMode;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /**
17
+ * The device's own light/dark preference, carried on the request.
18
+ *
19
+ * `prefers-color-scheme` is a media feature, and a media feature is a browser
20
+ * fact: a server render has no viewport to evaluate it against, so
21
+ * `useMediaQuery` reports light for every visitor whose device is dark. This
22
+ * user-agent client hint is the same preference in a form the server CAN read
23
+ * — the browser puts it on the request, so a visitor following their device
24
+ * gets their scheme in the first byte rather than a light document that
25
+ * repaints once React has hydrated the whole page.
26
+ *
27
+ * A browser sends it only where the origin asked for it, and the tenant
28
+ * middleware is what asks — it advertises this same token in `Accept-CH` and
29
+ * `Critical-CH` and splits its cache on it in `Vary`, from a copy of its own,
30
+ * since an edge bundle takes no library imports. Reading the request header by
31
+ * this constant is safe whatever the transport did to the casing: `Headers`
32
+ * matches case-insensitively.
33
+ *
34
+ * ⚠️ CHROMIUM ONLY. Firefox and Safari implement neither this hint nor the
35
+ * `Accept-CH` negotiation that asks for it, so on those browsers the header is
36
+ * simply absent and the scheme settles once the page has hydrated. Absence is
37
+ * therefore an ordinary answer, never an error: every reader must have a
38
+ * behavior for "the request did not say".
39
+ *
40
+ * Nothing in this module imports React, MUI or `js-cookie`, and its one import
41
+ * is a type erased at compile time. That is what lets a Server Component take
42
+ * it without pulling the theme library's context providers into the RSC graph
43
+ * — the same reason `util/theme-mode-cookie`, `util/emotion-cache` and
44
+ * `util/host-theme` are deep-imported rather than taken from the barrel.
45
+ */ export const COLOR_SCHEME_HINT_HEADER = 'Sec-CH-Prefers-Color-Scheme';
46
+ /**
47
+ * A `Sec-CH-Prefers-Color-Scheme` request-header value as a theme mode.
48
+ *
49
+ * Only `light` and `dark` are schemes; anything else — a header the browser
50
+ * never sent, a value from a future revision of the media feature — is `null`,
51
+ * meaning "the request named no scheme, fall back to the browser's own answer
52
+ * once there is one". A caller that mistook an unknown value for light would
53
+ * pin a dark device to the wrong scheme for the whole render.
54
+ *
55
+ * The value is a structured-field string, so it may arrive quoted (`"dark"`);
56
+ * the quotes are stripped and the token is compared case-insensitively so one
57
+ * parser reads every spelling a conforming user agent may send.
58
+ *
59
+ * This is the device-preference counterpart to `parseThemeModeCookie`, which
60
+ * reads the visitor's EXPLICIT choice off the same request. The two are
61
+ * separate answers on purpose and are never merged here: a stated preference
62
+ * outranks a device default, and the layering that enforces that lives in
63
+ * `useThemeModeState`, where the switcher can still tell them apart.
64
+ */ export function parseColorSchemeHint(value) {
65
+ const scheme = value == null ? void 0 : value.trim().replace(/^"|"$/g, '').toLowerCase();
66
+ return scheme === 'dark' || scheme === 'light' ? scheme : null;
67
+ }
68
+
69
+ //# sourceMappingURL=color-scheme-hint.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/color-scheme-hint.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { ThemeMode } from '../hocs/create-with-theme-provider'\n\n/**\n * The device's own light/dark preference, carried on the request.\n *\n * `prefers-color-scheme` is a media feature, and a media feature is a browser\n * fact: a server render has no viewport to evaluate it against, so\n * `useMediaQuery` reports light for every visitor whose device is dark. This\n * user-agent client hint is the same preference in a form the server CAN read\n * — the browser puts it on the request, so a visitor following their device\n * gets their scheme in the first byte rather than a light document that\n * repaints once React has hydrated the whole page.\n *\n * A browser sends it only where the origin asked for it, and the tenant\n * middleware is what asks — it advertises this same token in `Accept-CH` and\n * `Critical-CH` and splits its cache on it in `Vary`, from a copy of its own,\n * since an edge bundle takes no library imports. Reading the request header by\n * this constant is safe whatever the transport did to the casing: `Headers`\n * matches case-insensitively.\n *\n * ⚠️ CHROMIUM ONLY. Firefox and Safari implement neither this hint nor the\n * `Accept-CH` negotiation that asks for it, so on those browsers the header is\n * simply absent and the scheme settles once the page has hydrated. Absence is\n * therefore an ordinary answer, never an error: every reader must have a\n * behavior for \"the request did not say\".\n *\n * Nothing in this module imports React, MUI or `js-cookie`, and its one import\n * is a type erased at compile time. That is what lets a Server Component take\n * it without pulling the theme library's context providers into the RSC graph\n * — the same reason `util/theme-mode-cookie`, `util/emotion-cache` and\n * `util/host-theme` are deep-imported rather than taken from the barrel.\n */\nexport const COLOR_SCHEME_HINT_HEADER = 'Sec-CH-Prefers-Color-Scheme'\n\n/**\n * A `Sec-CH-Prefers-Color-Scheme` request-header value as a theme mode.\n *\n * Only `light` and `dark` are schemes; anything else — a header the browser\n * never sent, a value from a future revision of the media feature — is `null`,\n * meaning \"the request named no scheme, fall back to the browser's own answer\n * once there is one\". A caller that mistook an unknown value for light would\n * pin a dark device to the wrong scheme for the whole render.\n *\n * The value is a structured-field string, so it may arrive quoted (`\"dark\"`);\n * the quotes are stripped and the token is compared case-insensitively so one\n * parser reads every spelling a conforming user agent may send.\n *\n * This is the device-preference counterpart to `parseThemeModeCookie`, which\n * reads the visitor's EXPLICIT choice off the same request. The two are\n * separate answers on purpose and are never merged here: a stated preference\n * outranks a device default, and the layering that enforces that lives in\n * `useThemeModeState`, where the switcher can still tell them apart.\n */\nexport function parseColorSchemeHint(\n value: string | undefined | null,\n): ThemeMode {\n const scheme = value?.trim().replace(/^\"|\"$/g, '').toLowerCase()\n return scheme === 'dark' || scheme === 'light' ? scheme : null\n}\n"],"names":["COLOR_SCHEME_HINT_HEADER","parseColorSchemeHint","value","scheme","trim","replace","toLowerCase"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6BC,GACD,OAAO,MAAMA,2BAA2B,8BAA6B;AAErE;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,qBACdC,KAAgC;IAEhC,MAAMC,SAASD,yBAAAA,MAAOE,IAAI,GAAGC,OAAO,CAAC,UAAU,IAAIC,WAAW;IAC9D,OAAOH,WAAW,UAAUA,WAAW,UAAUA,SAAS;AAC5D"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { CssVarsThemeOptions } from '@mui/material/styles';
18
+ import { responsiveFontSizes, type Theme, type ThemeOptions } from '../../vendor/mui';
19
+ export type CreateResponsiveThemeOptions = {
20
+ themeOptions?: ThemeOptions;
21
+ responsiveFontSizesOptions?: Parameters<typeof responsiveFontSizes>[1];
22
+ };
23
+ /**
24
+ * START EXAMPLE – OVERRIDE DEFAULT PROPS ↓
25
+ * ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
26
+ * ```typescript
27
+ * const theme = createMuiTheme({
28
+ * props: {
29
+ * // Name of the component ⚛️
30
+ * MuiButtonBase: {
31
+ * // The default props to change
32
+ * disableRipple: true, // No more ripple, on the whole application 💣!
33
+ * },
34
+ * },
35
+ * })
36
+ * ```
37
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
38
+ * END EXAMPLE – OVERRIDE DEFAULT PROPS ↑
39
+ *
40
+ * START EXAMPLE – OVERRIDE DEFAULT STYLES ↓
41
+ * ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
42
+ * ```typescript
43
+ * const theme = createMuiTheme({
44
+ * overrides: {
45
+ * // Style sheet name ⚛️
46
+ * MuiButton: {
47
+ * // Name of the rule
48
+ * text: {
49
+ * // Some CSS
50
+ * color: 'white',
51
+ * },
52
+ * },
53
+ * MuiCssBaseline: {
54
+ * '@global': {
55
+ * html: {
56
+ * WebkitFontSmoothing: 'auto',
57
+ * },
58
+ * },
59
+ * },
60
+ * },
61
+ * })
62
+ * ```
63
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64
+ * END EXAMPLE – OVERRIDE DEFAULT STYLES ↑
65
+ *
66
+ * @param {ThemeOptions} options
67
+ * @returns {Theme}
68
+ */
69
+ export declare function createResponsiveTheme(options: CreateResponsiveThemeOptions): Theme;
70
+ export declare function createResponsiveCssVarTheme(light: Theme, dark: Theme, options?: CssVarsThemeOptions): Omit<Theme, "applyStyles"> & import("@mui/material").CssVarsTheme;
71
+ export default createResponsiveTheme;
@@ -0,0 +1,287 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
3
+ /**
4
+ * @license
5
+ * Copyright 2024 Aglyn LLC
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */ import { extendTheme as muiExtendTheme } from "@mui/material/styles";
19
+ import { createTheme, darken, getContrastRatio, lighten, responsiveFontSizes } from "../../vendor/mui.js";
20
+ import { accessibleShade, contrastRatio, meetsContrast } from "./accessible-shade.js";
21
+ var ContrastText = /*#__PURE__*/ function(ContrastText) {
22
+ ContrastText["LIGHT"] = "rgba(0, 0, 0, 0.87)";
23
+ ContrastText["DARK"] = "#FFFFFF";
24
+ return ContrastText;
25
+ }(ContrastText || {});
26
+ function resolveTonalOffsets(tonalOffset) {
27
+ var _ref, _ref1, _ref2;
28
+ const offsetObj = typeof tonalOffset === 'number' ? undefined : tonalOffset;
29
+ const offsetNum = typeof tonalOffset === 'number' ? tonalOffset : undefined;
30
+ return {
31
+ light: (_ref = (_ref1 = offsetObj == null ? void 0 : offsetObj.light) != null ? _ref1 : offsetNum) != null ? _ref : 0.2,
32
+ dark: (_ref2 = offsetObj == null ? void 0 : offsetObj.dark) != null ? _ref2 : (offsetNum != null ? offsetNum : 0.2) * 1.5
33
+ };
34
+ }
35
+ function getContrastTextColor(background, contrastThreshold) {
36
+ return getContrastRatio(background, "#FFFFFF") >= (contrastThreshold != null ? contrastThreshold : 3) ? "#FFFFFF" : "rgba(0, 0, 0, 0.87)";
37
+ }
38
+ function addShade(paletteColor, shade, variant, tonalOffset) {
39
+ // Custom palette colors (tertiary, surface) are optional in host themes.
40
+ if (!(paletteColor == null ? void 0 : paletteColor.main)) return;
41
+ const { light: tonalOffsetLight, dark: tonalOffsetDark } = resolveTonalOffsets(tonalOffset);
42
+ const indexed = paletteColor;
43
+ if (!indexed[shade]) {
44
+ // eslint-disable-next-line no-prototype-builtins
45
+ if (paletteColor.hasOwnProperty(variant)) {
46
+ indexed[shade] = indexed[variant];
47
+ } else if (shade === 'light') {
48
+ paletteColor.light = lighten(paletteColor.main, tonalOffsetLight);
49
+ } else if (shade === 'dark') {
50
+ paletteColor.dark = darken(paletteColor.main, tonalOffsetDark);
51
+ } else if (shade === 'contrastText') {
52
+ paletteColor.contrastText = getContrastTextColor(paletteColor.main, tonalOffsetDark);
53
+ }
54
+ }
55
+ }
56
+ function addShadeVariants(paletteColor, tonalOffset) {
57
+ addShade(paletteColor, 'dark', undefined, tonalOffset);
58
+ addShade(paletteColor, 'light', undefined, tonalOffset);
59
+ addShade(paletteColor, 'contrastText', undefined, tonalOffset);
60
+ }
61
+ /**
62
+ * Colour families whose shades act as FOREGROUNDS — the `dark` slot carries
63
+ * "accessible accent text" (AGL-1293 put the marketing brand blue there), so
64
+ * its derivation must clear AA against the scheme's real backgrounds.
65
+ *
66
+ * `surface` is deliberately absent: its `light`/`dark` are surface STEPS
67
+ * (panel fills, borders — see the designer's box styler), and walking them to
68
+ * text contrast would repaint layout chrome, not fix any text.
69
+ */ const FOREGROUND_COLOR_KEYS = [
70
+ 'primary',
71
+ 'secondary',
72
+ 'tertiary',
73
+ 'error',
74
+ 'warning',
75
+ 'info',
76
+ 'success'
77
+ ];
78
+ /** `contrastText` IS a text pairing wherever it lives, so `surface` joins here. */ const CONTRAST_TEXT_COLOR_KEYS = [
79
+ ...FOREGROUND_COLOR_KEYS,
80
+ 'surface'
81
+ ];
82
+ /**
83
+ * Post-`createTheme` pass making DERIVED shades scheme- and AA-aware
84
+ * (AGL-1297). Fixed tonal offsets derive `dark = darken(main, 0.3)` no
85
+ * matter the scheme, which (a) misses 4.5:1 on real off-white surfaces and
86
+ * (b) points the wrong way entirely in dark mode, where the accessible
87
+ * direction for a foreground shade is LIGHTER.
88
+ *
89
+ * Invariants:
90
+ * - Only slots ABSENT from the caller's `palette` input are ever touched;
91
+ * an explicitly authored shade passes through byte-identical (the
92
+ * marketing host's hand-set `primary.dark` values depend on this).
93
+ * - A derived value that already clears 4.5:1 against both
94
+ * `background.default` and `background.paper` is kept byte-identical, so
95
+ * themes that were fine yesterday do not shift.
96
+ * - A failing derived `dark` is re-seeded at the tonal offset from `main`
97
+ * in the scheme's foreground direction (darken in light, lighten in
98
+ * dark), then walked in HSL lightness until it clears the bar.
99
+ * - A light-scheme `light` is a background TINT — its ramp direction points
100
+ * away from the text bar by design — so it keeps the tonal derivation.
101
+ * In dark schemes `light` is foreground-capable and gets the same walk.
102
+ * - Derived `contrastText` keeps the threshold choice unless that pairing
103
+ * is below 4.5:1 against `main`; then it walks toward the pole with more
104
+ * contrast headroom.
105
+ */ function ensureAccessibleShades(theme, inputPalette) {
106
+ var _palette_background, _palette_background1;
107
+ const { palette } = theme;
108
+ const backgrounds = [
109
+ (_palette_background = palette.background) == null ? void 0 : _palette_background.default,
110
+ (_palette_background1 = palette.background) == null ? void 0 : _palette_background1.paper
111
+ ].filter((background)=>typeof background === 'string');
112
+ if (!backgrounds.length) return;
113
+ const mode = palette.mode === 'dark' ? 'dark' : 'light';
114
+ const foregroundDirection = mode === 'dark' ? 'lighten' : 'darken';
115
+ const tonalOffsetDark = resolveTonalOffsets(palette.tonalOffset).dark;
116
+ const providedColors = inputPalette;
117
+ for (const key of CONTRAST_TEXT_COLOR_KEYS){
118
+ const color = palette[key];
119
+ if (!color || typeof color.main !== 'string') continue;
120
+ const provided = providedColors == null ? void 0 : providedColors[key];
121
+ // A ColorPartial ramp (`{ 50…900 }`) is an author-owned scale — MUI maps
122
+ // its shades straight across, so nothing in it counts as derived.
123
+ if (provided && typeof provided === 'object' && !('main' in provided)) {
124
+ continue;
125
+ }
126
+ // Read with brackets, not dots: these keys come from an index signature,
127
+ // and `json-editor` compiles this file under
128
+ // `noPropertyAccessFromIndexSignature` (paths resolve to source, so a
129
+ // consumer's flags check its dependencies). Dot access reds that build
130
+ // while this project's own stays green — AGL-1323.
131
+ const explicit = provided != null ? provided : {};
132
+ const isForeground = FOREGROUND_COLOR_KEYS.includes(key);
133
+ try {
134
+ if (isForeground && !explicit['dark'] && typeof color.dark === 'string' && !meetsContrast(color.dark, backgrounds)) {
135
+ const seed = mode === 'dark' ? lighten(color.main, tonalOffsetDark) : color.dark;
136
+ color.dark = accessibleShade(seed, backgrounds, foregroundDirection);
137
+ }
138
+ if (isForeground && mode === 'dark' && !explicit['light'] && typeof color.light === 'string' && !meetsContrast(color.light, backgrounds)) {
139
+ color.light = accessibleShade(color.light, backgrounds, 'lighten');
140
+ }
141
+ if (!explicit['contrastText'] && typeof color.contrastText === 'string' && !meetsContrast(color.contrastText, [
142
+ color.main
143
+ ])) {
144
+ const textDirection = contrastRatio('#fff', color.main) >= contrastRatio('#000', color.main) ? 'lighten' : 'darken';
145
+ color.contrastText = accessibleShade(color.contrastText, [
146
+ color.main
147
+ ], textDirection);
148
+ }
149
+ } catch (unused) {
150
+ // Unparseable colour (CSS variable, color-mix()): leave the derived
151
+ // value alone rather than failing theme creation.
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * MUI's own default `responsiveFontSizes` variant list, spelled out because
157
+ * passing `variants` replaces it rather than extending it.
158
+ */ const RAMPED_MUI_VARIANTS = [
159
+ 'h1',
160
+ 'h2',
161
+ 'h3',
162
+ 'h4',
163
+ 'h5',
164
+ 'h6',
165
+ 'subtitle1',
166
+ 'subtitle2',
167
+ 'body1',
168
+ 'body2',
169
+ 'caption',
170
+ 'button',
171
+ 'overline'
172
+ ];
173
+ /**
174
+ * The brand's custom display rungs, which MUI cannot know about.
175
+ *
176
+ * Only variants whose size genuinely needs to shrink belong here. `lede`,
177
+ * `bodyCompact` and `micro` are body-scale text that reads the same at every
178
+ * width and are deliberately absent.
179
+ */ const RAMPED_DISPLAY_VARIANTS = [
180
+ 'displayXl'
181
+ ];
182
+ /**
183
+ * START EXAMPLE – OVERRIDE DEFAULT PROPS ↓
184
+ * ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
185
+ * ```typescript
186
+ * const theme = createMuiTheme({
187
+ * props: {
188
+ * // Name of the component ⚛️
189
+ * MuiButtonBase: {
190
+ * // The default props to change
191
+ * disableRipple: true, // No more ripple, on the whole application 💣!
192
+ * },
193
+ * },
194
+ * })
195
+ * ```
196
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
197
+ * END EXAMPLE – OVERRIDE DEFAULT PROPS ↑
198
+ *
199
+ * START EXAMPLE – OVERRIDE DEFAULT STYLES ↓
200
+ * ⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄⌄
201
+ * ```typescript
202
+ * const theme = createMuiTheme({
203
+ * overrides: {
204
+ * // Style sheet name ⚛️
205
+ * MuiButton: {
206
+ * // Name of the rule
207
+ * text: {
208
+ * // Some CSS
209
+ * color: 'white',
210
+ * },
211
+ * },
212
+ * MuiCssBaseline: {
213
+ * '@global': {
214
+ * html: {
215
+ * WebkitFontSmoothing: 'auto',
216
+ * },
217
+ * },
218
+ * },
219
+ * },
220
+ * })
221
+ * ```
222
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
223
+ * END EXAMPLE – OVERRIDE DEFAULT STYLES ↑
224
+ *
225
+ * @param {ThemeOptions} options
226
+ * @returns {Theme}
227
+ */ export function createResponsiveTheme(options) {
228
+ const { themeOptions, responsiveFontSizesOptions } = options;
229
+ let theme = createTheme(themeOptions);
230
+ addShadeVariants(theme.palette.tertiary, theme.palette.tonalOffset);
231
+ addShadeVariants(theme.palette.surface, theme.palette.tonalOffset);
232
+ // `tint` is deliberately absent from BOTH shade passes (AGL-1244). It is a
233
+ // group of string leaves rather than a PaletteColor — no `main` to ramp
234
+ // from, no `contrastText` to pair — so `addShadeVariants` would no-op and
235
+ // `ensureAccessibleShades` (which keys off `CONTRAST_TEXT_COLOR_KEYS`) never
236
+ // sees it. Both correct: a 90%-desaturated wash is authored, not derived,
237
+ // and walking it to a text-contrast bar would repaint the fill.
238
+ // Scheme- and AA-aware repair of DERIVED shades only (AGL-1297); explicit
239
+ // palette values pass through byte-identical.
240
+ ensureAccessibleShades(theme, themeOptions == null ? void 0 : themeOptions.palette);
241
+ theme = responsiveFontSizes(theme, _extends({
242
+ // Override to include `xs` and `xl` - default: ['sm', 'md', 'lg']
243
+ breakpoints: [
244
+ 'xs',
245
+ 'sm',
246
+ 'md',
247
+ 'lg',
248
+ 'xl'
249
+ ],
250
+ // MUI only ramps the variants it ships, so a CUSTOM display variant would
251
+ // keep its desktop size on a phone — `displayXl` at 72px on a 375px
252
+ // screen. Passing `variants` REPLACES MUI's default list, so the defaults
253
+ // are spelled out here and ours appended; drop one and that variant
254
+ // silently stops scaling.
255
+ variants: [
256
+ ...RAMPED_MUI_VARIANTS,
257
+ ...RAMPED_DISPLAY_VARIANTS
258
+ ]
259
+ }, responsiveFontSizesOptions));
260
+ return theme;
261
+ }
262
+ export function createResponsiveCssVarTheme(light, dark, options) {
263
+ var _ref, _ref1;
264
+ var _options_colorSchemes, _options_colorSchemes1;
265
+ const { palette: lightPalette } = light, lightTheme = _object_without_properties_loose(light, [
266
+ "palette"
267
+ ]);
268
+ const { palette: darkPalette } = dark;
269
+ return muiExtendTheme(_extends({}, lightTheme, {
270
+ // Allow setMode() to work by driving the color scheme via a CSS class on
271
+ // <html> instead of the OS-level @media query (the default 'media' selector
272
+ // makes setMode() a no-op because media queries can't be overridden in JS).
273
+ colorSchemeSelector: 'class'
274
+ }, options, {
275
+ colorSchemes: _extends({}, options == null ? void 0 : options.colorSchemes, {
276
+ light: _extends({
277
+ palette: lightPalette
278
+ }, (_ref = options == null ? void 0 : (_options_colorSchemes = options.colorSchemes) == null ? void 0 : _options_colorSchemes.light) != null ? _ref : {}),
279
+ dark: _extends({
280
+ palette: darkPalette
281
+ }, (_ref1 = options == null ? void 0 : (_options_colorSchemes1 = options.colorSchemes) == null ? void 0 : _options_colorSchemes1.dark) != null ? _ref1 : {})
282
+ })
283
+ }));
284
+ }
285
+ export default createResponsiveTheme;
286
+
287
+ //# sourceMappingURL=create-responsive-theme.js.map