@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,95 @@
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
+ /**
18
+ * The visitor's scheme, as a path segment (AGL-2708).
19
+ *
20
+ * A published page resolves its dark scheme in JS rather than in CSS, so the
21
+ * scheme has to be decided before the first render. Reading it from the
22
+ * request inside the render — `cookies()` in a layout — decides it correctly
23
+ * but makes the route dynamic, and the catch-all page beneath declares
24
+ * `revalidate`: on the regeneration path Next renders that segment in a static
25
+ * context, where a dynamic API throws `DYNAMIC_SERVER_USAGE`. Per-visitor
26
+ * theming and ONE cached document are irreconcilable.
27
+ *
28
+ * Per-SCHEME theming and TWO cached documents are not. This module is that
29
+ * seam: the middleware resolves the scheme from the request — where reading
30
+ * cookies and headers costs nothing, because middleware runs ahead of the
31
+ * cache on every request — and spends it as a path segment. Next's route cache
32
+ * keys on the pathname, so `light` and `dark` become two entries of the same
33
+ * page instead of one entry that cannot answer either.
34
+ *
35
+ * ⚠️ TWO SEGMENTS, NOT FOUR. The request carries two separate answers — the
36
+ * visitor's explicit choice (cookie) and their device's preference (client
37
+ * hint) — and `useThemeModeState` keeps them apart so the switcher can show
38
+ * "Device default" checked rather than the scheme the device happens to be in.
39
+ * Encoding both would be four cache entries per page. This encodes only what
40
+ * they RESOLVE to, because that is the only part that changes a pixel: which
41
+ * radio is checked is settled at hydration from the cookie the browser already
42
+ * has, inside a menu a visitor has to open before they can see it. Four-fold
43
+ * cache multiplication is a poor price for the checked state of a hidden
44
+ * control, on a page that is billed per view.
45
+ *
46
+ * Nothing here imports React, MUI or `js-cookie`, and its one import is a type
47
+ * erased at compile time — the same constraint `util/theme-mode-cookie` and
48
+ * `util/color-scheme-hint` hold themselves to, and for the same two reasons:
49
+ * a Server Component may take it without pulling the theme library's context
50
+ * providers into the RSC graph, and the edge middleware may take it at all.
51
+ */
52
+ import type { ThemeMode } from '../hocs/create-with-theme-provider';
53
+ /**
54
+ * Every spelling the segment is allowed to take.
55
+ *
56
+ * A closed set on purpose: the segment is written by our own middleware and
57
+ * read back by a route, so an unknown value is never a visitor's preference —
58
+ * it is a stale link, a crawler walking a guessed URL, or a bug. Each such
59
+ * request must still render a page, which is what `parseSchemeRouteSegment`
60
+ * guarantees by resolving anything unrecognized to the light default rather
61
+ * than throwing.
62
+ */
63
+ export declare const SCHEME_ROUTE_SEGMENTS: readonly ["light", "dark"];
64
+ export type SchemeRouteSegment = (typeof SCHEME_ROUTE_SEGMENTS)[number];
65
+ /**
66
+ * The scheme a request resolves to, as the segment that will carry it.
67
+ *
68
+ * This is the same precedence `useThemeModeState` applies on the client — a
69
+ * stated preference outranks a device default, and light is what remains when
70
+ * the request said neither — expressed once here so the server render and the
71
+ * hydration that follows it cannot disagree about which document was owed.
72
+ *
73
+ * `'system'` is not a scheme. The switcher writes it to mean "follow the
74
+ * device", and `parseThemeModeCookie` already reads it back as `null`; it is
75
+ * rejected here too so a caller passing a raw cookie value cannot smuggle it
76
+ * in as a choice.
77
+ *
78
+ * ⚠️ Light for a request that named nothing is a FALLBACK, not a default
79
+ * preference. Firefox and Safari send no client hint, so their visitors reach
80
+ * this with two nulls and are served the light document, then settle on their
81
+ * real device scheme once `prefers-color-scheme` can be evaluated — the same
82
+ * hydration-time settle those browsers have always had here. Choosing dark
83
+ * instead would not fix them; it would only move the flip to the other half of
84
+ * their visitors.
85
+ */
86
+ export declare function resolveSchemeRouteSegment(chosen: ThemeMode, device: ThemeMode): SchemeRouteSegment;
87
+ /**
88
+ * A path segment as the scheme to render.
89
+ *
90
+ * Total, by construction: an absent, misspelled or hostile segment renders the
91
+ * light document rather than refusing. A route that 500s on an unrecognized
92
+ * segment would turn a stale bookmark into an error page, and this segment is
93
+ * in the URL of every cached page on the site.
94
+ */
95
+ export declare function parseSchemeRouteSegment(value: string | undefined | null): SchemeRouteSegment;
@@ -0,0 +1,101 @@
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 visitor's scheme, as a path segment (AGL-2708).
18
+ *
19
+ * A published page resolves its dark scheme in JS rather than in CSS, so the
20
+ * scheme has to be decided before the first render. Reading it from the
21
+ * request inside the render — `cookies()` in a layout — decides it correctly
22
+ * but makes the route dynamic, and the catch-all page beneath declares
23
+ * `revalidate`: on the regeneration path Next renders that segment in a static
24
+ * context, where a dynamic API throws `DYNAMIC_SERVER_USAGE`. Per-visitor
25
+ * theming and ONE cached document are irreconcilable.
26
+ *
27
+ * Per-SCHEME theming and TWO cached documents are not. This module is that
28
+ * seam: the middleware resolves the scheme from the request — where reading
29
+ * cookies and headers costs nothing, because middleware runs ahead of the
30
+ * cache on every request — and spends it as a path segment. Next's route cache
31
+ * keys on the pathname, so `light` and `dark` become two entries of the same
32
+ * page instead of one entry that cannot answer either.
33
+ *
34
+ * ⚠️ TWO SEGMENTS, NOT FOUR. The request carries two separate answers — the
35
+ * visitor's explicit choice (cookie) and their device's preference (client
36
+ * hint) — and `useThemeModeState` keeps them apart so the switcher can show
37
+ * "Device default" checked rather than the scheme the device happens to be in.
38
+ * Encoding both would be four cache entries per page. This encodes only what
39
+ * they RESOLVE to, because that is the only part that changes a pixel: which
40
+ * radio is checked is settled at hydration from the cookie the browser already
41
+ * has, inside a menu a visitor has to open before they can see it. Four-fold
42
+ * cache multiplication is a poor price for the checked state of a hidden
43
+ * control, on a page that is billed per view.
44
+ *
45
+ * Nothing here imports React, MUI or `js-cookie`, and its one import is a type
46
+ * erased at compile time — the same constraint `util/theme-mode-cookie` and
47
+ * `util/color-scheme-hint` hold themselves to, and for the same two reasons:
48
+ * a Server Component may take it without pulling the theme library's context
49
+ * providers into the RSC graph, and the edge middleware may take it at all.
50
+ */ /**
51
+ * Every spelling the segment is allowed to take.
52
+ *
53
+ * A closed set on purpose: the segment is written by our own middleware and
54
+ * read back by a route, so an unknown value is never a visitor's preference —
55
+ * it is a stale link, a crawler walking a guessed URL, or a bug. Each such
56
+ * request must still render a page, which is what `parseSchemeRouteSegment`
57
+ * guarantees by resolving anything unrecognized to the light default rather
58
+ * than throwing.
59
+ */ export const SCHEME_ROUTE_SEGMENTS = [
60
+ 'light',
61
+ 'dark'
62
+ ];
63
+ /**
64
+ * The scheme a request resolves to, as the segment that will carry it.
65
+ *
66
+ * This is the same precedence `useThemeModeState` applies on the client — a
67
+ * stated preference outranks a device default, and light is what remains when
68
+ * the request said neither — expressed once here so the server render and the
69
+ * hydration that follows it cannot disagree about which document was owed.
70
+ *
71
+ * `'system'` is not a scheme. The switcher writes it to mean "follow the
72
+ * device", and `parseThemeModeCookie` already reads it back as `null`; it is
73
+ * rejected here too so a caller passing a raw cookie value cannot smuggle it
74
+ * in as a choice.
75
+ *
76
+ * ⚠️ Light for a request that named nothing is a FALLBACK, not a default
77
+ * preference. Firefox and Safari send no client hint, so their visitors reach
78
+ * this with two nulls and are served the light document, then settle on their
79
+ * real device scheme once `prefers-color-scheme` can be evaluated — the same
80
+ * hydration-time settle those browsers have always had here. Choosing dark
81
+ * instead would not fix them; it would only move the flip to the other half of
82
+ * their visitors.
83
+ */ export function resolveSchemeRouteSegment(chosen, device) {
84
+ const scheme = isScheme(chosen) ? chosen : isScheme(device) ? device : 'light';
85
+ return scheme;
86
+ }
87
+ /**
88
+ * A path segment as the scheme to render.
89
+ *
90
+ * Total, by construction: an absent, misspelled or hostile segment renders the
91
+ * light document rather than refusing. A route that 500s on an unrecognized
92
+ * segment would turn a stale bookmark into an error page, and this segment is
93
+ * in the URL of every cached page on the site.
94
+ */ export function parseSchemeRouteSegment(value) {
95
+ return value === 'dark' ? 'dark' : 'light';
96
+ }
97
+ function isScheme(mode) {
98
+ return mode === 'light' || mode === 'dark';
99
+ }
100
+
101
+ //# sourceMappingURL=scheme-route-segment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/scheme-route-segment.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\n/**\n * The visitor's scheme, as a path segment (AGL-2708).\n *\n * A published page resolves its dark scheme in JS rather than in CSS, so the\n * scheme has to be decided before the first render. Reading it from the\n * request inside the render — `cookies()` in a layout — decides it correctly\n * but makes the route dynamic, and the catch-all page beneath declares\n * `revalidate`: on the regeneration path Next renders that segment in a static\n * context, where a dynamic API throws `DYNAMIC_SERVER_USAGE`. Per-visitor\n * theming and ONE cached document are irreconcilable.\n *\n * Per-SCHEME theming and TWO cached documents are not. This module is that\n * seam: the middleware resolves the scheme from the request — where reading\n * cookies and headers costs nothing, because middleware runs ahead of the\n * cache on every request — and spends it as a path segment. Next's route cache\n * keys on the pathname, so `light` and `dark` become two entries of the same\n * page instead of one entry that cannot answer either.\n *\n * ⚠️ TWO SEGMENTS, NOT FOUR. The request carries two separate answers — the\n * visitor's explicit choice (cookie) and their device's preference (client\n * hint) — and `useThemeModeState` keeps them apart so the switcher can show\n * \"Device default\" checked rather than the scheme the device happens to be in.\n * Encoding both would be four cache entries per page. This encodes only what\n * they RESOLVE to, because that is the only part that changes a pixel: which\n * radio is checked is settled at hydration from the cookie the browser already\n * has, inside a menu a visitor has to open before they can see it. Four-fold\n * cache multiplication is a poor price for the checked state of a hidden\n * control, on a page that is billed per view.\n *\n * Nothing here imports React, MUI or `js-cookie`, and its one import is a type\n * erased at compile time — the same constraint `util/theme-mode-cookie` and\n * `util/color-scheme-hint` hold themselves to, and for the same two reasons:\n * a Server Component may take it without pulling the theme library's context\n * providers into the RSC graph, and the edge middleware may take it at all.\n */\nimport type { ThemeMode } from '../hocs/create-with-theme-provider'\n\n/**\n * Every spelling the segment is allowed to take.\n *\n * A closed set on purpose: the segment is written by our own middleware and\n * read back by a route, so an unknown value is never a visitor's preference —\n * it is a stale link, a crawler walking a guessed URL, or a bug. Each such\n * request must still render a page, which is what `parseSchemeRouteSegment`\n * guarantees by resolving anything unrecognized to the light default rather\n * than throwing.\n */\nexport const SCHEME_ROUTE_SEGMENTS = ['light', 'dark'] as const\n\nexport type SchemeRouteSegment = (typeof SCHEME_ROUTE_SEGMENTS)[number]\n\n/**\n * The scheme a request resolves to, as the segment that will carry it.\n *\n * This is the same precedence `useThemeModeState` applies on the client — a\n * stated preference outranks a device default, and light is what remains when\n * the request said neither — expressed once here so the server render and the\n * hydration that follows it cannot disagree about which document was owed.\n *\n * `'system'` is not a scheme. The switcher writes it to mean \"follow the\n * device\", and `parseThemeModeCookie` already reads it back as `null`; it is\n * rejected here too so a caller passing a raw cookie value cannot smuggle it\n * in as a choice.\n *\n * ⚠️ Light for a request that named nothing is a FALLBACK, not a default\n * preference. Firefox and Safari send no client hint, so their visitors reach\n * this with two nulls and are served the light document, then settle on their\n * real device scheme once `prefers-color-scheme` can be evaluated — the same\n * hydration-time settle those browsers have always had here. Choosing dark\n * instead would not fix them; it would only move the flip to the other half of\n * their visitors.\n */\nexport function resolveSchemeRouteSegment(\n chosen: ThemeMode,\n device: ThemeMode,\n): SchemeRouteSegment {\n const scheme = isScheme(chosen) ? chosen : isScheme(device) ? device : 'light'\n return scheme\n}\n\n/**\n * A path segment as the scheme to render.\n *\n * Total, by construction: an absent, misspelled or hostile segment renders the\n * light document rather than refusing. A route that 500s on an unrecognized\n * segment would turn a stale bookmark into an error page, and this segment is\n * in the URL of every cached page on the site.\n */\nexport function parseSchemeRouteSegment(\n value: string | undefined | null,\n): SchemeRouteSegment {\n return value === 'dark' ? 'dark' : 'light'\n}\n\nfunction isScheme(mode: ThemeMode): mode is SchemeRouteSegment {\n return mode === 'light' || mode === 'dark'\n}\n"],"names":["SCHEME_ROUTE_SEGMENTS","resolveSchemeRouteSegment","chosen","device","scheme","isScheme","parseSchemeRouteSegment","value","mode"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCC,GAGD;;;;;;;;;CASC,GACD,OAAO,MAAMA,wBAAwB;IAAC;IAAS;CAAO,CAAS;AAI/D;;;;;;;;;;;;;;;;;;;;CAoBC,GACD,OAAO,SAASC,0BACdC,MAAiB,EACjBC,MAAiB;IAEjB,MAAMC,SAASC,SAASH,UAAUA,SAASG,SAASF,UAAUA,SAAS;IACvE,OAAOC;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASE,wBACdC,KAAgC;IAEhC,OAAOA,UAAU,SAAS,SAAS;AACrC;AAEA,SAASF,SAASG,IAAe;IAC/B,OAAOA,SAAS,WAAWA,SAAS;AACtC"}
@@ -0,0 +1,41 @@
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 { HostThemeScheme } from '@aglyn/shared-data-types';
18
+ import { type ThemeColorToken } from './theme-editor-fields';
19
+ /** The color a slot renders in one scheme when the site leaves it unset. */
20
+ export declare function inheritedThemeColor(scheme: HostThemeScheme, token: ThemeColorToken, host?: string): string | undefined;
21
+ /**
22
+ * The corner radius every site ships with. Read rather than written as a
23
+ * literal, so the default the editor shows cannot stop matching the theme.
24
+ *
25
+ * One value for every site, unlike the colors above: the tenant default
26
+ * changes the PALETTE and takes the rest of its options — shape, spacing,
27
+ * type ramp, component behaviour — from the same place the brand does.
28
+ */
29
+ export declare const INHERITED_BORDER_RADIUS: number;
30
+ /** The spacing unit the brand theme ships. */
31
+ export declare const INHERITED_SPACING: number;
32
+ /**
33
+ * The first family of the brand's font stack — what "Theme default" means in
34
+ * the font select, named rather than left as a long CSS list.
35
+ */
36
+ export declare const INHERITED_FONT_FAMILY: string;
37
+ /** MUI's own toolbar heights, which apply while the site sets none. */
38
+ export declare const INHERITED_TOOLBAR_HEIGHTS: {
39
+ readonly xs: 56;
40
+ readonly sm: 64;
41
+ };
@@ -0,0 +1,49 @@
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
+ */ var _ref;
17
+ var _consoleOptions_shape, _consoleOptions_typography;
18
+ import { consoleOptions } from "../console.theme.js";
19
+ import { siteFallbackTheme } from "../tenant.theme.js";
20
+ import { DEFAULT_TOOLBAR_SM, DEFAULT_TOOLBAR_XS } from "./theme-editor-fields.js";
21
+ function builtPalette(scheme, host) {
22
+ return siteFallbackTheme(host, scheme).palette;
23
+ }
24
+ /** The color a slot renders in one scheme when the site leaves it unset. */ export function inheritedThemeColor(scheme, token, host) {
25
+ var _palette_group;
26
+ const palette = builtPalette(scheme, host);
27
+ const [group, key] = token.split('.');
28
+ const value = token === 'divider' ? palette['divider'] : (_palette_group = palette[group]) == null ? void 0 : _palette_group[key != null ? key : 'main'];
29
+ return typeof value === 'string' ? value : undefined;
30
+ }
31
+ /**
32
+ * The corner radius every site ships with. Read rather than written as a
33
+ * literal, so the default the editor shows cannot stop matching the theme.
34
+ *
35
+ * One value for every site, unlike the colors above: the tenant default
36
+ * changes the PALETTE and takes the rest of its options — shape, spacing,
37
+ * type ramp, component behaviour — from the same place the brand does.
38
+ */ export const INHERITED_BORDER_RADIUS = typeof ((_consoleOptions_shape = consoleOptions.shape) == null ? void 0 : _consoleOptions_shape.borderRadius) === 'number' ? consoleOptions.shape.borderRadius : 4;
39
+ /** The spacing unit the brand theme ships. */ export const INHERITED_SPACING = typeof consoleOptions.spacing === 'number' ? consoleOptions.spacing : 8;
40
+ /**
41
+ * The first family of the brand's font stack — what "Theme default" means in
42
+ * the font select, named rather than left as a long CSS list.
43
+ */ export const INHERITED_FONT_FAMILY = String((_ref = (_consoleOptions_typography = consoleOptions.typography) == null ? void 0 : _consoleOptions_typography.fontFamily) != null ? _ref : '').split(',')[0].replace(/["']/g, '').trim();
44
+ /** MUI's own toolbar heights, which apply while the site sets none. */ export const INHERITED_TOOLBAR_HEIGHTS = {
45
+ xs: DEFAULT_TOOLBAR_XS,
46
+ sm: DEFAULT_TOOLBAR_SM
47
+ };
48
+
49
+ //# sourceMappingURL=theme-editor-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../../libs/shared/ui/theme/src/lib/util/theme-editor-defaults.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 { HostThemeScheme } from '@aglyn/shared-data-types'\nimport { consoleOptions } from '../console.theme'\nimport { siteFallbackTheme } from '../tenant.theme'\nimport {\n DEFAULT_TOOLBAR_SM,\n DEFAULT_TOOLBAR_XS,\n type ThemeColorToken,\n} from './theme-editor-fields'\n\n/**\n * What each theme editor control resolves to when the site sets nothing\n * (AGL-1180, AGL-2938): the theme that site is layered over.\n *\n * Read off the BUILT themes rather than the options, because text, divider\n * and the light and dark shades are derived by MUI, and the raw options would\n * leave those slots with nothing to report. The editor shows these beside\n * every \"Default\", and anything that proposes a change to a site's theme\n * reads the same values, so \"the default primary\" is one color wherever it\n * is named.\n *\n * WHICH theme is a property of the site, not of the platform (AGL-3068): the\n * operator's own hosts are layered over the brand, every customer site over\n * the neutral tenant default. Named by its site key, and unnamed resolves\n * what a customer site resolves — a \"Default\" that reported the brand to a\n * site publishing in the tenant palette named a color that site never draws.\n *\n * Kept apart from `theme-editor-fields.ts` because this module builds the\n * themes when it loads, and the catalog is read where no theme needs\n * building.\n */\n\ntype PaletteRecord = Record<string, unknown>\n\nfunction builtPalette(\n scheme: HostThemeScheme,\n host: string | undefined,\n): PaletteRecord {\n return siteFallbackTheme(host, scheme).palette as unknown as PaletteRecord\n}\n\n/** The color a slot renders in one scheme when the site leaves it unset. */\nexport function inheritedThemeColor(\n scheme: HostThemeScheme,\n token: ThemeColorToken,\n host?: string,\n): string | undefined {\n const palette = builtPalette(scheme, host)\n const [group, key] = token.split('.')\n const value =\n token === 'divider'\n ? palette['divider']\n : (palette[group] as PaletteRecord | undefined)?.[key ?? 'main']\n return typeof value === 'string' ? value : undefined\n}\n\n/**\n * The corner radius every site ships with. Read rather than written as a\n * literal, so the default the editor shows cannot stop matching the theme.\n *\n * One value for every site, unlike the colors above: the tenant default\n * changes the PALETTE and takes the rest of its options — shape, spacing,\n * type ramp, component behaviour — from the same place the brand does.\n */\nexport const INHERITED_BORDER_RADIUS =\n typeof consoleOptions.shape?.borderRadius === 'number'\n ? consoleOptions.shape.borderRadius\n : 4\n\n/** The spacing unit the brand theme ships. */\nexport const INHERITED_SPACING =\n typeof consoleOptions.spacing === 'number' ? consoleOptions.spacing : 8\n\n/**\n * The first family of the brand's font stack — what \"Theme default\" means in\n * the font select, named rather than left as a long CSS list.\n */\nexport const INHERITED_FONT_FAMILY = String(\n (consoleOptions.typography as { fontFamily?: string } | undefined)\n ?.fontFamily ?? '',\n)\n .split(',')[0]\n .replace(/[\"']/g, '')\n .trim()\n\n/** MUI's own toolbar heights, which apply while the site sets none. */\nexport const INHERITED_TOOLBAR_HEIGHTS = {\n xs: DEFAULT_TOOLBAR_XS,\n sm: DEFAULT_TOOLBAR_SM,\n} as const\n"],"names":["consoleOptions","siteFallbackTheme","DEFAULT_TOOLBAR_SM","DEFAULT_TOOLBAR_XS","builtPalette","scheme","host","palette","inheritedThemeColor","token","group","key","split","value","undefined","INHERITED_BORDER_RADIUS","shape","borderRadius","INHERITED_SPACING","spacing","INHERITED_FONT_FAMILY","String","typography","fontFamily","replace","trim","INHERITED_TOOLBAR_HEIGHTS","xs","sm"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC;IAkEQA,uBAaNA;AA5EH,SAASA,cAAc,QAAQ,sBAAkB;AACjD,SAASC,iBAAiB,QAAQ,qBAAiB;AACnD,SACEC,kBAAkB,EAClBC,kBAAkB,QAEb,2BAAuB;AA0B9B,SAASC,aACPC,MAAuB,EACvBC,IAAwB;IAExB,OAAOL,kBAAkBK,MAAMD,QAAQE,OAAO;AAChD;AAEA,0EAA0E,GAC1E,OAAO,SAASC,oBACdH,MAAuB,EACvBI,KAAsB,EACtBH,IAAa;QAONC;IALP,MAAMA,UAAUH,aAAaC,QAAQC;IACrC,MAAM,CAACI,OAAOC,IAAI,GAAGF,MAAMG,KAAK,CAAC;IACjC,MAAMC,QACJJ,UAAU,YACNF,OAAO,CAAC,UAAU,IACjBA,iBAAAA,OAAO,CAACG,MAAM,qBAAf,AAACH,cAA8C,CAACI,cAAAA,MAAO,OAAO;IACpE,OAAO,OAAOE,UAAU,WAAWA,QAAQC;AAC7C;AAEA;;;;;;;CAOC,GACD,OAAO,MAAMC,0BACX,SAAOf,wBAAAA,eAAegB,KAAK,qBAApBhB,sBAAsBiB,YAAY,MAAK,WAC1CjB,eAAegB,KAAK,CAACC,YAAY,GACjC,EAAC;AAEP,4CAA4C,GAC5C,OAAO,MAAMC,oBACX,OAAOlB,eAAemB,OAAO,KAAK,WAAWnB,eAAemB,OAAO,GAAG,EAAC;AAEzE;;;CAGC,GACD,OAAO,MAAMC,wBAAwBC,gBAClCrB,6BAAAA,eAAesB,UAAU,qBAA1B,AAACtB,2BACGuB,UAAU,mBAAI,IAEjBX,KAAK,CAAC,IAAI,CAAC,EAAE,CACbY,OAAO,CAAC,SAAS,IACjBC,IAAI,GAAE;AAET,qEAAqE,GACrE,OAAO,MAAMC,4BAA4B;IACvCC,IAAIxB;IACJyB,IAAI1B;AACN,EAAU"}
@@ -0,0 +1,315 @@
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 { HostTheme, HostThemeScheme, HostThemeSchemeColors } from '@aglyn/shared-data-types';
18
+ import { type HostThemeComponentKey } from './host-theme';
19
+ /**
20
+ * The host theme editor's field catalog (AGL-2938): every control the editor
21
+ * renders, the range each one accepts, the token it writes, and the write
22
+ * itself.
23
+ *
24
+ * It lives here rather than beside the editor because the editor is not the
25
+ * only thing that changes a site's theme. Anything that proposes a theme
26
+ * change has to offer exactly the controls the editor offers, with the same
27
+ * bounds, and has to write each one the way the editor writes it. A copy of
28
+ * this list anywhere else is a second answer to "what can a theme hold" that
29
+ * drifts from the first the day a control is added, so the editor renders
30
+ * from this module and every other writer reads it.
31
+ *
32
+ * Pure data and pure functions over `HostTheme`: no MUI, no React and no
33
+ * brand theme. What a control resolves to when the site sets nothing is the
34
+ * brand theme's business, in `theme-editor-defaults.ts`.
35
+ *
36
+ * Deliberately NOT re-exported from this library's index: the index reaches
37
+ * every published page, and nothing a visitor renders needs the catalog.
38
+ * Reach it as `@aglyn/shared-ui-theme/util/theme-editor-fields`.
39
+ */
40
+ /**
41
+ * MUI's own toolbar breakpoint. `mixins.toolbar` has to carry this exact
42
+ * query, because the rule it competes with is MUI's `@media (min-width:600px)
43
+ * { min-height: 64px }` (AGL-1242).
44
+ */
45
+ export declare const TOOLBAR_SM_MIN_WIDTH = 600;
46
+ export declare const TOOLBAR_SM_QUERY = "@media (min-width:600px)";
47
+ /** MUI's stock Toolbar heights, shown when the host has set none. */
48
+ export declare const DEFAULT_TOOLBAR_XS = 56;
49
+ export declare const DEFAULT_TOOLBAR_SM = 64;
50
+ /**
51
+ * `createMixins` spreads `...mixins` AFTER its default, so anything we write
52
+ * REPLACES the stock toolbar wholesale — including its short-landscape rule.
53
+ * Carrying it forward keeps that behavior instead of dropping it silently.
54
+ */
55
+ export declare const TOOLBAR_LANDSCAPE_QUERY = "@media (min-width:0px)";
56
+ export declare const TOOLBAR_LANDSCAPE_RULE: {
57
+ '@media (orientation: landscape)': {
58
+ minHeight: number;
59
+ };
60
+ };
61
+ /** Reads a px `minHeight` out of `mixins.toolbar` for one breakpoint. */
62
+ export declare function readToolbarHeight(theme: HostTheme, breakpoint: 'xs' | 'sm'): number;
63
+ /**
64
+ * Builds a COMPLETE `mixins.toolbar`, in the order MUI emits it.
65
+ *
66
+ * Two things this exists to get right, both because `mixins.toolbar` replaces
67
+ * MUI's default wholesale rather than merging into it (AGL-1242):
68
+ *
69
+ * - **Completeness.** Anything omitted is simply gone. Writing only a desktop
70
+ * height left portrait phones with no `min-height` at all, so unset
71
+ * breakpoints fall back to MUI's own 56 / 64.
72
+ * - **Key order.** These land in one CSS rule, so the last matching
73
+ * declaration wins. MUI emits the landscape clause BEFORE the sm height;
74
+ * emitting it after makes a wide landscape window — i.e. every desktop —
75
+ * 48px tall.
76
+ */
77
+ export declare function buildToolbarMixin(xs: number | undefined, sm: number | undefined): {
78
+ minHeight: string;
79
+ "@media (min-width:0px)": {
80
+ '@media (orientation: landscape)': {
81
+ minHeight: number;
82
+ };
83
+ };
84
+ "@media (min-width:600px)": {
85
+ minHeight: string;
86
+ };
87
+ };
88
+ /**
89
+ * Serializes the branches whose KEY ORDER changes what renders.
90
+ *
91
+ * `deepEqual` is order-INsensitive on purpose (AGL-56: Firestore hands the
92
+ * palette back in a different key order than the editor builds it, and a
93
+ * string compare left Save enabled forever), so on its own it cannot see a
94
+ * reorder of `mixins.toolbar` — a change that really does alter the CSS.
95
+ *
96
+ * Deliberately narrow: only `mixins`. `components.*.styleOverrides` are CSS
97
+ * objects too, but widening this would re-expose the AGL-56 failure for every
98
+ * host with component overrides, and order-sensitivity has only actually been
99
+ * demonstrated here.
100
+ *
101
+ * Firestore does NOT hand a map back in the order it was written, and it
102
+ * hands different readers different orders (AGL-3146) — which is why what
103
+ * renders is canonicalized in `hostThemeToThemeOptions` rather than trusted
104
+ * from storage. A freshly-loaded document is still not dirty here, because
105
+ * the draft is seeded from the loaded document itself: both sides of this
106
+ * comparison carry whatever order THIS reader was given.
107
+ */
108
+ export declare function orderSensitiveKey(theme: HostTheme): string;
109
+ export type PaletteColorKey = 'primary' | 'secondary' | 'tertiary' | 'surface' | 'error' | 'warning' | 'info' | 'success';
110
+ export declare const PALETTE_COLOR_FIELDS: Array<{
111
+ key: PaletteColorKey;
112
+ label: string;
113
+ }>;
114
+ export type SurfaceColorPath = ['background', 'default'] | ['background', 'paper'] | ['text', 'primary'] | ['text', 'secondary'] | ['text', 'disabled'] | ['tint', 'primary'] | ['tint', 'secondary'] | ['tint', 'tertiary'];
115
+ export declare const SURFACE_COLOR_FIELDS: Array<{
116
+ path: SurfaceColorPath;
117
+ label: string;
118
+ }>;
119
+ /**
120
+ * Pale accent washes (AGL-1244).
121
+ *
122
+ * Separate from {@link PALETTE_COLOR_FIELDS} because a tint is a STRING LEAF,
123
+ * not a `{ main }` record — the palette fields all write `main`, and a tint has
124
+ * none. It rides the same `SurfaceColorPath` machinery as `background`/`text`
125
+ * for exactly that reason, and is listed apart only so the editor can head it
126
+ * "Tints" instead of filing it under "Background & text".
127
+ */
128
+ export declare const TINT_COLOR_FIELDS: Array<{
129
+ path: SurfaceColorPath;
130
+ label: string;
131
+ }>;
132
+ /** The one color leaf that is neither a palette color nor a member of a group. */
133
+ export declare const DIVIDER_COLOR_FIELD: {
134
+ readonly key: "divider";
135
+ readonly label: "Divider";
136
+ };
137
+ /** Curated Google Fonts choices for the font family selector. */
138
+ export declare const GOOGLE_FONT_OPTIONS: Array<{
139
+ family: string;
140
+ category: 'sans-serif' | 'serif' | 'monospace' | 'display';
141
+ weights: Array<number>;
142
+ }>;
143
+ export declare function fontFamilyStack(family: string, category: (typeof GOOGLE_FONT_OPTIONS)[number]['category']): string;
144
+ export declare function getSchemeColor(colors: HostThemeSchemeColors | undefined, path: SurfaceColorPath): string | undefined;
145
+ /** The two color schemes the editor edits, in tab order. */
146
+ export declare const THEME_EDITOR_SCHEMES: readonly HostThemeScheme[];
147
+ /**
148
+ * Every color the editor edits per scheme, named the way a diff names it: a
149
+ * palette key (`primary`), a group path joined with a dot
150
+ * (`background.default`, `tint.primary`), or `divider`.
151
+ */
152
+ export type ThemeColorToken = PaletteColorKey | 'background.default' | 'background.paper' | 'text.primary' | 'text.secondary' | 'text.disabled' | 'tint.primary' | 'tint.secondary' | 'tint.tertiary' | 'divider';
153
+ /** Where a color control sits in the editor's Color scheme card. */
154
+ export type ThemeColorGroup = 'palette' | 'surface' | 'tint' | 'divider';
155
+ /** Every color field the editor renders, in the order it renders them. */
156
+ export declare const THEME_COLOR_FIELDS: ReadonlyArray<{
157
+ token: ThemeColorToken;
158
+ label: string;
159
+ group: ThemeColorGroup;
160
+ }>;
161
+ /** Whether visitors get a dark scheme; absent from the document means `auto`. */
162
+ export type ThemeDarkSchemeValue = 'auto' | 'off';
163
+ export declare const DARK_SCHEME_FIELD: {
164
+ readonly label: "Dark scheme";
165
+ readonly options: readonly [{
166
+ readonly value: "auto";
167
+ readonly label: "Follows the visitor";
168
+ }, {
169
+ readonly value: "off";
170
+ readonly label: "Off — always light";
171
+ }];
172
+ };
173
+ /**
174
+ * The font select's value for "no font of the site's own": the theme's
175
+ * default stack, with nothing to load.
176
+ */
177
+ export declare const SYSTEM_FONT_VALUE = "__system__";
178
+ export declare const FONT_FAMILY_FIELD: {
179
+ readonly label: "Font family";
180
+ };
181
+ /** The corner radius slider, in px. */
182
+ export declare const BORDER_RADIUS_FIELD: {
183
+ readonly label: "Border radius";
184
+ readonly min: 0;
185
+ readonly max: 24;
186
+ readonly step: 1;
187
+ };
188
+ /** The spacing unit MUI multiplies every `spacing(n)` by, in px. */
189
+ export declare const SPACING_FIELD: {
190
+ readonly label: "Spacing unit (px)";
191
+ readonly min: 2;
192
+ readonly max: 16;
193
+ readonly step: 1;
194
+ };
195
+ /** The two nav heights, either side of MUI's toolbar breakpoint, in px. */
196
+ export declare const TOOLBAR_HEIGHT_FIELDS: {
197
+ readonly xs: {
198
+ readonly label: "Nav height, mobile (px)";
199
+ readonly min: 40;
200
+ readonly max: 160;
201
+ readonly step: 1;
202
+ };
203
+ readonly sm: {
204
+ readonly label: "Nav height, desktop (px)";
205
+ readonly min: 40;
206
+ readonly max: 160;
207
+ readonly step: 1;
208
+ };
209
+ };
210
+ /** The raw-JSON component overrides, limited to the sanitizer's whitelist. */
211
+ export declare const COMPONENT_OVERRIDES_FIELD: {
212
+ readonly label: "Component overrides";
213
+ readonly components: readonly ["MuiAppBar", "MuiAvatar", "MuiBadge", "MuiButton", "MuiButtonBase", "MuiCard", "MuiCardContent", "MuiCheckbox", "MuiChip", "MuiCircularProgress", "MuiDivider", "MuiIconButton", "MuiLinearProgress", "MuiLink", "MuiList", "MuiListItem", "MuiMenu", "MuiPaper", "MuiRadio", "MuiSlider", "MuiSwitch", "MuiTab", "MuiTabs", "MuiTextField", "MuiToolbar", "MuiTooltip", "MuiTypography"];
214
+ };
215
+ /**
216
+ * The media queries a component override may scope a style to: either side
217
+ * of MUI's own toolbar breakpoint, the one breakpoint the editor already
218
+ * names. MUI's `down('sm')` stops a twentieth of a pixel short of it.
219
+ */
220
+ export declare const THEME_EDITOR_MEDIA_QUERIES: {
221
+ readonly mobile: "@media (max-width:599.95px)";
222
+ readonly desktop: "@media (min-width:600px)";
223
+ };
224
+ export type ThemeEditorMedia = keyof typeof THEME_EDITOR_MEDIA_QUERIES;
225
+ export type ThemeEditorControlId = `color.${ThemeColorToken}` | 'darkScheme' | 'fontFamily' | 'borderRadius' | 'spacing' | 'navHeight.xs' | 'navHeight.sm' | 'components';
226
+ /** One control the editor renders, described well enough to be offered elsewhere. */
227
+ export interface ThemeEditorControl {
228
+ /** Stable id: what a diff row, a proposal and a spec name the control by. */
229
+ id: ThemeEditorControlId;
230
+ /** The label the editor shows. */
231
+ label: string;
232
+ kind: 'color' | 'select' | 'number' | 'json';
233
+ /** Edited once per scheme, under the Light and Dark tabs. */
234
+ perScheme: boolean;
235
+ /** A color control's token. */
236
+ token?: ThemeColorToken;
237
+ /** A select's values, or the components a JSON control accepts. */
238
+ options?: readonly string[];
239
+ /** A number control's inclusive bounds and its step. */
240
+ min?: number;
241
+ max?: number;
242
+ step?: number;
243
+ }
244
+ /**
245
+ * Every value-bearing control the editor renders, in the order it renders
246
+ * them. The buttons that act on these values rather than hold one — Copy
247
+ * from light, Reset to default, Discard, Save — are not controls: a reset is
248
+ * a control set back to nothing, and a copy is every color control of one
249
+ * scheme written from the other.
250
+ */
251
+ export declare const THEME_EDITOR_CONTROLS: readonly ThemeEditorControl[];
252
+ /** The catalog entry for a control id. */
253
+ export declare function themeEditorControl(id: ThemeEditorControlId): ThemeEditorControl;
254
+ /** A color the site set for one scheme; `undefined` when the slot inherits. */
255
+ export declare function readThemeColor(theme: HostTheme | undefined, scheme: HostThemeScheme, token: ThemeColorToken): string | undefined;
256
+ /**
257
+ * Sets one color for one scheme, or clears it with `undefined` so the slot
258
+ * inherits again. A palette color writes `main` and keeps whatever else its
259
+ * record carries, and clearing it drops the record; a group member that
260
+ * leaves its group empty drops the group.
261
+ */
262
+ export declare function writeThemeColor(theme: HostTheme, scheme: HostThemeScheme, token: ThemeColorToken, hex: string | undefined): HostTheme;
263
+ /** Replaces one scheme's colors with a copy of the other's. */
264
+ export declare function copyThemeSchemeColors(theme: HostTheme, from: HostThemeScheme, to: HostThemeScheme): HostTheme;
265
+ /** The dark scheme switch as the select shows it. */
266
+ export declare function readDarkScheme(theme: HostTheme | undefined): ThemeDarkSchemeValue;
267
+ /**
268
+ * Absent means "follows the visitor"; only the opt-out is written, so the
269
+ * saved document stays empty for the common case.
270
+ */
271
+ export declare function writeDarkScheme(theme: HostTheme, value: ThemeDarkSchemeValue): HostTheme;
272
+ /** The font select's value: the site's font, or {@link SYSTEM_FONT_VALUE}. */
273
+ export declare function readFontFamily(theme: HostTheme | undefined): string;
274
+ /**
275
+ * Picks a font from {@link GOOGLE_FONT_OPTIONS}, loading it and naming it as
276
+ * the typography's stack, or {@link SYSTEM_FONT_VALUE} to go back to the
277
+ * theme default. A family outside the curated list changes nothing.
278
+ */
279
+ export declare function writeFontFamily(theme: HostTheme, value: string): HostTheme;
280
+ /** Sets the corner radius, or clears it with `undefined` so it inherits. */
281
+ export declare function writeBorderRadius(theme: HostTheme, value: number | undefined): HostTheme;
282
+ /** Sets the spacing unit; anything but a positive number clears it. */
283
+ export declare function writeSpacing(theme: HostTheme, value: number | undefined): HostTheme;
284
+ /**
285
+ * Sets one nav height — anything but a positive number clears it — and
286
+ * rebuilds the whole toolbar mixin around both, because `mixins.toolbar`
287
+ * replaces MUI's default rather than merging into it (AGL-1242).
288
+ */
289
+ export declare function writeToolbarHeight(theme: HostTheme, breakpoint: 'xs' | 'sm', value: number | undefined): HostTheme;
290
+ /**
291
+ * Drops the site's component overrides. With nothing stored the site renders
292
+ * the brand's own component styles, which is what the editor's "Reset to
293
+ * theme defaults" means.
294
+ */
295
+ export declare function resetComponentOverrides(theme: HostTheme): HostTheme;
296
+ /** One leaf of a component override, as a writer names it. */
297
+ export interface ThemeComponentOverrideLeaf {
298
+ component: HostThemeComponentKey;
299
+ /** `styleOverrides` styles a slot; `defaultProps` sets a prop's default. */
300
+ target: 'styleOverrides' | 'defaultProps';
301
+ /** The style slot (`root`, `contained`, `h1`); `null` for a default prop. */
302
+ slot: string | null;
303
+ /** A camelCase CSS property, or the prop's name. */
304
+ property: string;
305
+ /** Scopes a style to one side of the toolbar breakpoint; `null` for every width. */
306
+ media: ThemeEditorMedia | null;
307
+ value: string | number | boolean;
308
+ }
309
+ /**
310
+ * Sets one leaf inside the site's component overrides and keeps every other
311
+ * override the site has. The stored overrides are deep-merged over the
312
+ * brand's own at render time, so naming one property changes that property
313
+ * and inherits the rest of the component.
314
+ */
315
+ export declare function writeComponentOverride(theme: HostTheme, leaf: ThemeComponentOverrideLeaf): HostTheme;