@cas-smartdesign/design-tokens 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tokens.css CHANGED
@@ -442,7 +442,7 @@ body:not([sd-color-scheme]), [sd-color-scheme="light dark"]:not(.prefers-dark),
442
442
  /** Another background color for less prominent actions; Action = do something */
443
443
  --color-bg-action-neutral-weakest: var(--base-color-white-opaque);
444
444
  /** Hover state for color/bg/action-neutralWeakest */
445
- --color-bg-action-neutral-weakest-hover: var(--base-color-grey-150);
445
+ --color-bg-action-neutral-weakest-hover: var(--base-color-grey-100);
446
446
  /** Pressed state for color/bg/action-neutralWeakest */
447
447
  --color-bg-action-neutral-weakest-pressed: var(--base-color-grey-250);
448
448
  /** Background color for notification messages, not customizable. The color does not suggest interactivity. */
@@ -464,9 +464,9 @@ body:not([sd-color-scheme]), [sd-color-scheme="light dark"]:not(.prefers-dark),
464
464
  /** Hover state for color/bg/selection-accentWeak */
465
465
  --color-bg-selection-accent-weak-hover: var(--base-color-dark-blue-100);
466
466
  /** Third background color for components that allow selection; Selection = choose something */
467
- --color-bg-selection-neutral-default: var(--base-color-grey-150);
467
+ --color-bg-selection-neutral-default: var(--base-color-grey-200);
468
468
  /** Hover state for color/bg/selection-neutralDefault */
469
- --color-bg-selection-neutral-default-hover: var(--base-color-grey-250);
469
+ --color-bg-selection-neutral-default-hover: var(--base-color-grey-100);
470
470
  /** Fourth background color for components that allow selection; Selection = choose something */
471
471
  --color-bg-selection-neutral-weakest: var(--base-color-white-opaque);
472
472
  /** Background color for success messages. The color does not suggest interactivity. */
@@ -759,9 +759,9 @@ body:not([sd-color-scheme]), [sd-color-scheme="light dark"]:not(.prefers-dark),
759
759
  /** Another background color for less prominent actions; Action = do something */
760
760
  --color-bg-action-neutral-weakest: var(--base-color-grey-800);
761
761
  /** Hover state for color/bg/action-neutralWeakest */
762
- --color-bg-action-neutral-weakest-hover: var(--base-color-grey-700);
762
+ --color-bg-action-neutral-weakest-hover: var(--base-color-grey-750);
763
763
  /** Pressed state for color/bg/action-neutralWeakest */
764
- --color-bg-action-neutral-weakest-pressed: var(--base-color-grey-750);
764
+ --color-bg-action-neutral-weakest-pressed: var(--base-color-grey-650);
765
765
  /** Background color for notification messages, not customizable. The color does not suggest interactivity. */
766
766
  --color-bg-assistive: var(--base-color-blue-750);
767
767
  /** Brand-based background color, primarily for the header area. The color does not suggest interactivity. */
@@ -926,58 +926,58 @@ body:not([sd-color-scheme]), [sd-color-scheme="light dark"]:not(.prefers-dark),
926
926
  --color-rainbow-blue-strong10: var(--base-color-blue-400);
927
927
  --color-rainbow-blue-strong20: var(--base-color-blue-350);
928
928
  --color-rainbow-blue-strong30: var(--base-color-blue-200);
929
- --color-rainbow-blue-weak10: var(--base-color-blue-650);
930
- --color-rainbow-blue-weak20: var(--base-color-blue-700);
931
- --color-rainbow-blue-weak30: var(--base-color-blue-750);
932
- --color-rainbow-blue-weak40: var(--base-color-blue-800);
933
- --color-rainbow-brown-default: var(--base-color-brown-500);
934
- --color-rainbow-brown-strong10: var(--base-color-brown-400);
935
- --color-rainbow-brown-strong20: var(--base-color-brown-350);
936
- --color-rainbow-brown-strong30: var(--base-color-brown-200);
937
- --color-rainbow-brown-weak10: var(--base-color-brown-650);
938
- --color-rainbow-brown-weak20: var(--base-color-brown-700);
939
- --color-rainbow-brown-weak30: var(--base-color-brown-750);
940
- --color-rainbow-brown-weak40: var(--base-color-brown-800);
941
- --color-rainbow-cyan-default: var(--base-color-cyan-500);
942
- --color-rainbow-cyan-strong10: var(--base-color-cyan-400);
943
- --color-rainbow-cyan-strong20: var(--base-color-cyan-350);
944
- --color-rainbow-cyan-strong30: var(--base-color-cyan-200);
945
- --color-rainbow-cyan-weak10: var(--base-color-cyan-650);
946
- --color-rainbow-cyan-weak20: var(--base-color-cyan-700);
947
- --color-rainbow-cyan-weak30: var(--base-color-cyan-750);
948
- --color-rainbow-cyan-weak40: var(--base-color-cyan-800);
949
- --color-rainbow-dark-blue-default: var(--base-color-dark-blue-500);
950
- --color-rainbow-dark-blue-strong10: var(--base-color-dark-blue-400);
951
- --color-rainbow-dark-blue-strong20: var(--base-color-dark-blue-350);
952
- --color-rainbow-dark-blue-strong30: var(--base-color-dark-blue-200);
953
- --color-rainbow-dark-blue-weak10: var(--base-color-dark-blue-650);
954
- --color-rainbow-dark-blue-weak20: var(--base-color-dark-blue-700);
955
- --color-rainbow-dark-blue-weak30: var(--base-color-dark-blue-750);
956
- --color-rainbow-dark-blue-weak40: var(--base-color-dark-blue-800);
957
- --color-rainbow-dark-green-default: var(--base-color-dark-green-500);
958
- --color-rainbow-dark-green-strong10: var(--base-color-dark-green-400);
959
- --color-rainbow-dark-green-strong20: var(--base-color-dark-green-350);
960
- --color-rainbow-dark-green-strong30: var(--base-color-dark-green-200);
961
- --color-rainbow-dark-green-weak10: var(--base-color-dark-green-650);
962
- --color-rainbow-dark-green-weak20: var(--base-color-dark-green-700);
963
- --color-rainbow-dark-green-weak30: var(--base-color-dark-green-750);
964
- --color-rainbow-dark-green-weak40: var(--base-color-dark-green-800);
929
+ --color-rainbow-blue-weak10: var(--base-color-blue-600);
930
+ --color-rainbow-blue-weak20: var(--base-color-blue-650);
931
+ --color-rainbow-blue-weak30: var(--base-color-blue-700);
932
+ --color-rainbow-blue-weak40: var(--base-color-blue-750);
933
+ --color-rainbow-brown-default: var(--base-color-brown-350);
934
+ --color-rainbow-brown-strong10: var(--base-color-brown-300);
935
+ --color-rainbow-brown-strong20: var(--base-color-brown-250);
936
+ --color-rainbow-brown-strong30: var(--base-color-brown-100);
937
+ --color-rainbow-brown-weak10: var(--base-color-brown-400);
938
+ --color-rainbow-brown-weak20: var(--base-color-brown-500);
939
+ --color-rainbow-brown-weak30: var(--base-color-brown-600);
940
+ --color-rainbow-brown-weak40: var(--base-color-brown-650);
941
+ --color-rainbow-cyan-default: var(--base-color-cyan-400);
942
+ --color-rainbow-cyan-strong10: var(--base-color-cyan-350);
943
+ --color-rainbow-cyan-strong20: var(--base-color-cyan-300);
944
+ --color-rainbow-cyan-strong30: var(--base-color-cyan-150);
945
+ --color-rainbow-cyan-weak10: var(--base-color-cyan-500);
946
+ --color-rainbow-cyan-weak20: var(--base-color-cyan-600);
947
+ --color-rainbow-cyan-weak30: var(--base-color-cyan-650);
948
+ --color-rainbow-cyan-weak40: var(--base-color-cyan-700);
949
+ --color-rainbow-dark-blue-default: var(--base-color-dark-blue-350);
950
+ --color-rainbow-dark-blue-strong10: var(--base-color-dark-blue-300);
951
+ --color-rainbow-dark-blue-strong20: var(--base-color-dark-blue-250);
952
+ --color-rainbow-dark-blue-strong30: var(--base-color-dark-blue-100);
953
+ --color-rainbow-dark-blue-weak10: var(--base-color-dark-blue-400);
954
+ --color-rainbow-dark-blue-weak20: var(--base-color-dark-blue-500);
955
+ --color-rainbow-dark-blue-weak30: var(--base-color-dark-blue-600);
956
+ --color-rainbow-dark-blue-weak40: var(--base-color-dark-blue-650);
957
+ --color-rainbow-dark-green-default: var(--base-color-dark-green-400);
958
+ --color-rainbow-dark-green-strong10: var(--base-color-dark-green-350);
959
+ --color-rainbow-dark-green-strong20: var(--base-color-dark-green-300);
960
+ --color-rainbow-dark-green-strong30: var(--base-color-dark-green-150);
961
+ --color-rainbow-dark-green-weak10: var(--base-color-dark-green-500);
962
+ --color-rainbow-dark-green-weak20: var(--base-color-dark-green-600);
963
+ --color-rainbow-dark-green-weak30: var(--base-color-dark-green-650);
964
+ --color-rainbow-dark-green-weak40: var(--base-color-dark-green-700);
965
965
  --color-rainbow-green-default: var(--base-color-green-500);
966
966
  --color-rainbow-green-strong10: var(--base-color-green-400);
967
967
  --color-rainbow-green-strong20: var(--base-color-green-350);
968
968
  --color-rainbow-green-strong30: var(--base-color-green-200);
969
- --color-rainbow-green-weak10: var(--base-color-green-650);
970
- --color-rainbow-green-weak20: var(--base-color-green-700);
971
- --color-rainbow-green-weak30: var(--base-color-green-750);
972
- --color-rainbow-green-weak40: var(--base-color-green-800);
969
+ --color-rainbow-green-weak10: var(--base-color-green-600);
970
+ --color-rainbow-green-weak20: var(--base-color-green-650);
971
+ --color-rainbow-green-weak30: var(--base-color-green-700);
972
+ --color-rainbow-green-weak40: var(--base-color-green-750);
973
973
  --color-rainbow-grey-default: var(--base-color-grey-500);
974
974
  --color-rainbow-grey-strong10: var(--base-color-grey-400);
975
975
  --color-rainbow-grey-strong20: var(--base-color-grey-350);
976
976
  --color-rainbow-grey-strong30: var(--base-color-grey-200);
977
- --color-rainbow-grey-weak10: var(--base-color-grey-650);
978
- --color-rainbow-grey-weak20: var(--base-color-grey-700);
979
- --color-rainbow-grey-weak30: var(--base-color-grey-750);
980
- --color-rainbow-grey-weak40: var(--base-color-grey-800);
977
+ --color-rainbow-grey-weak10: var(--base-color-grey-600);
978
+ --color-rainbow-grey-weak20: var(--base-color-grey-650);
979
+ --color-rainbow-grey-weak30: var(--base-color-grey-700);
980
+ --color-rainbow-grey-weak40: var(--base-color-grey-750);
981
981
  /** Strong contrasting neutral color, mostly for outlines. Intended for special cases, that do not justify an extra semantic token, e.g. for outlining the selected app tile. */
982
982
  --color-rainbow-mono-strong: var(--base-color-white-opaque);
983
983
  /** Weak contrasting neutral color, mostly for border. Camouflage with bg/default. Intended for special cases, that do not justify an extra semantic token, e.g. for separating a foreground element from its background. */
@@ -986,44 +986,44 @@ body:not([sd-color-scheme]), [sd-color-scheme="light dark"]:not(.prefers-dark),
986
986
  --color-rainbow-orange-strong10: var(--base-color-orange-400);
987
987
  --color-rainbow-orange-strong20: var(--base-color-orange-350);
988
988
  --color-rainbow-orange-strong30: var(--base-color-orange-200);
989
- --color-rainbow-orange-weak10: var(--base-color-orange-650);
990
- --color-rainbow-orange-weak20: var(--base-color-orange-700);
991
- --color-rainbow-orange-weak30: var(--base-color-orange-750);
992
- --color-rainbow-orange-weak40: var(--base-color-orange-800);
993
- --color-rainbow-pink-default: var(--base-color-pink-500);
994
- --color-rainbow-pink-strong10: var(--base-color-pink-400);
995
- --color-rainbow-pink-strong20: var(--base-color-pink-350);
996
- --color-rainbow-pink-strong30: var(--base-color-pink-200);
997
- --color-rainbow-pink-weak10: var(--base-color-pink-650);
998
- --color-rainbow-pink-weak20: var(--base-color-pink-700);
999
- --color-rainbow-pink-weak30: var(--base-color-pink-750);
1000
- --color-rainbow-pink-weak40: var(--base-color-pink-800);
1001
- --color-rainbow-purple-default: var(--base-color-purple-500);
1002
- --color-rainbow-purple-strong10: var(--base-color-purple-400);
1003
- --color-rainbow-purple-strong20: var(--base-color-purple-350);
1004
- --color-rainbow-purple-strong30: var(--base-color-purple-200);
1005
- --color-rainbow-purple-weak10: var(--base-color-purple-650);
1006
- --color-rainbow-purple-weak20: var(--base-color-purple-650);
1007
- --color-rainbow-purple-weak30: var(--base-color-purple-750);
1008
- --color-rainbow-purple-weak40: var(--base-color-purple-800);
1009
- --color-rainbow-red-default: var(--base-color-red-500);
1010
- --color-rainbow-red-strong10: var(--base-color-red-400);
1011
- --color-rainbow-red-strong20: var(--base-color-red-350);
1012
- --color-rainbow-red-strong30: var(--base-color-red-200);
1013
- --color-rainbow-red-weak10: var(--base-color-red-650);
1014
- --color-rainbow-red-weak20: var(--base-color-red-700);
1015
- --color-rainbow-red-weak30: var(--base-color-red-750);
1016
- --color-rainbow-red-weak40: var(--base-color-red-800);
989
+ --color-rainbow-orange-weak10: var(--base-color-orange-600);
990
+ --color-rainbow-orange-weak20: var(--base-color-orange-650);
991
+ --color-rainbow-orange-weak30: var(--base-color-orange-700);
992
+ --color-rainbow-orange-weak40: var(--base-color-orange-750);
993
+ --color-rainbow-pink-default: var(--base-color-pink-350);
994
+ --color-rainbow-pink-strong10: var(--base-color-pink-300);
995
+ --color-rainbow-pink-strong20: var(--base-color-pink-250);
996
+ --color-rainbow-pink-strong30: var(--base-color-pink-100);
997
+ --color-rainbow-pink-weak10: var(--base-color-pink-400);
998
+ --color-rainbow-pink-weak20: var(--base-color-pink-500);
999
+ --color-rainbow-pink-weak30: var(--base-color-pink-600);
1000
+ --color-rainbow-pink-weak40: var(--base-color-pink-650);
1001
+ --color-rainbow-purple-default: var(--base-color-purple-350);
1002
+ --color-rainbow-purple-strong10: var(--base-color-purple-300);
1003
+ --color-rainbow-purple-strong20: var(--base-color-purple-250);
1004
+ --color-rainbow-purple-strong30: var(--base-color-purple-100);
1005
+ --color-rainbow-purple-weak10: var(--base-color-purple-400);
1006
+ --color-rainbow-purple-weak20: var(--base-color-purple-500);
1007
+ --color-rainbow-purple-weak30: var(--base-color-purple-600);
1008
+ --color-rainbow-purple-weak40: var(--base-color-purple-650);
1009
+ --color-rainbow-red-default: var(--base-color-red-400);
1010
+ --color-rainbow-red-strong10: var(--base-color-red-350);
1011
+ --color-rainbow-red-strong20: var(--base-color-red-300);
1012
+ --color-rainbow-red-strong30: var(--base-color-red-150);
1013
+ --color-rainbow-red-weak10: var(--base-color-red-500);
1014
+ --color-rainbow-red-weak20: var(--base-color-red-600);
1015
+ --color-rainbow-red-weak30: var(--base-color-red-650);
1016
+ --color-rainbow-red-weak40: var(--base-color-red-700);
1017
1017
  /** On dark backgrounds. For the foreground and the border, which are placed on a colored background that does not change with the design options. The selection is based on contrast. */
1018
1018
  --color-rainbow-white: var(--base-color-white-opaque);
1019
1019
  --color-rainbow-yellow-default: var(--base-color-yellow-500);
1020
1020
  --color-rainbow-yellow-strong10: var(--base-color-yellow-400);
1021
1021
  --color-rainbow-yellow-strong20: var(--base-color-yellow-350);
1022
1022
  --color-rainbow-yellow-strong30: var(--base-color-yellow-200);
1023
- --color-rainbow-yellow-weak10: var(--base-color-yellow-650);
1024
- --color-rainbow-yellow-weak20: var(--base-color-yellow-700);
1025
- --color-rainbow-yellow-weak30: var(--base-color-yellow-750);
1026
- --color-rainbow-yellow-weak40: var(--base-color-yellow-800);
1023
+ --color-rainbow-yellow-weak10: var(--base-color-yellow-600);
1024
+ --color-rainbow-yellow-weak20: var(--base-color-yellow-650);
1025
+ --color-rainbow-yellow-weak30: var(--base-color-yellow-700);
1026
+ --color-rainbow-yellow-weak40: var(--base-color-yellow-750);
1027
1027
  --color-rainbow-ai-default: linear-gradient(90deg, var(--color-rainbow-ai-default-gradient-start) 0%, var(--color-rainbow-ai-default-gradient-end) 100%);
1028
1028
  --color-rainbow-ai-strong10: linear-gradient(90deg, var(--color-rainbow-ai-strong10-gradient-start) 0%, var(--color-rainbow-ai-strong10-gradient-end) 100%);
1029
1029
  --color-rainbow-ai-strong20: linear-gradient(90deg, var(--color-rainbow-ai-strong20-gradient-start) 0%, var(--color-rainbow-ai-strong20-gradient-end) 100%);
package/package.json CHANGED
@@ -1,14 +1,20 @@
1
1
  {
2
2
  "name": "@cas-smartdesign/design-tokens",
3
- "version": "3.0.3",
3
+ "version": "3.0.5",
4
4
  "description": "Design tokens for SmartDesign",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "files": [
7
7
  "dist",
8
+ "theme-switcher.mjs",
9
+ "theme-switcher.d.ts",
8
10
  "npm-third-party-licenses.json"
9
11
  ],
10
12
  "type": "module",
11
13
  "exports": {
14
+ "./theme-switcher": {
15
+ "types": "./theme-switcher.d.ts",
16
+ "default": "./theme-switcher.mjs"
17
+ },
12
18
  "./css": {
13
19
  "types": "./dist/tokens.d.ts",
14
20
  "default": "./dist/tokens.css"
@@ -40,7 +46,7 @@
40
46
  "@types/node": "^25.5.2",
41
47
  "marked": "^18.0.0",
42
48
  "style-dictionary": "^5.4.0",
43
- "@cas-smartdesign/license-generator": "^1.10.0"
49
+ "@cas-smartdesign/license-generator": "^1.10.2"
44
50
  },
45
51
  "scripts": {
46
52
  "version": "pnpm version",
@@ -0,0 +1,3 @@
1
+ // Side-effect only module: applies the persisted sd-color-scheme preference and renders
2
+ // the theme switcher (unless the page is embedded). See theme-switcher.mjs.
3
+ export {};
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Light / dark / auto switcher for the SmartDesign color-scheme protocol.
3
+ *
4
+ * Importing this module applies the persisted preference and renders a fixed switcher in
5
+ * the top-right corner of the page:
6
+ *
7
+ * - the `sd-color-scheme` attribute on <body> holds the preference
8
+ * ("light" | "dark" | "light dark" for auto) — the selectors of the generated token CSS
9
+ * key off it (see build.mts),
10
+ * - the `prefers-dark` class on <body> mirrors the OS preference,
11
+ * - the preference is persisted under the "sd-color-scheme" localStorage key.
12
+ *
13
+ * Embedding: when the page runs inside an iframe no UI is rendered — the host page owns
14
+ * the switcher and controls the embedded page by posting
15
+ * `{ type: "sd-color-scheme", preference }` messages. Every applied preference change is
16
+ * announced as a "sd-color-scheme-change" CustomEvent on window, which host pages (e.g.
17
+ * the aggregated documentation index) use to forward the preference to embedded pages.
18
+ *
19
+ * Shipped as a hand-written, dependency-free ES module so it can be used without any
20
+ * build step (plain <script type="module">, inlining, or bundlers alike).
21
+ *
22
+ * Styling: importing this module does NOT load the token CSS — that is the page's own
23
+ * concern (the ./css export). The switcher styles therefore use the token custom
24
+ * properties with light-dark() fallbacks, so the switcher stays usable on pages that do
25
+ * not load the tokens (e.g. the aggregated documentation index).
26
+ */
27
+
28
+ const STORAGE_KEY = "sd-color-scheme";
29
+ const MESSAGE_TYPE = "sd-color-scheme";
30
+ const CHANGE_EVENT = "sd-color-scheme-change";
31
+ const COLOR_SCHEMES = [
32
+ ["light", "☀️ light"],
33
+ ["dark", "🌙 dark"],
34
+ ["light dark", "🌓 auto"],
35
+ ];
36
+
37
+ /* The light-dark() fallbacks mirror the light/dark values of the referenced tokens
38
+ (resolved from dist/{light,dark}/tokens.css) so pages without the token CSS render
39
+ identically to pages with it — keep them in sync when the tokens change. */
40
+ const switcherStyle = `
41
+ .theme-switcher {
42
+ position: fixed;
43
+ top: 16px;
44
+ right: 16px;
45
+ z-index: 100;
46
+ display: flex;
47
+ gap: 4px;
48
+ padding: 4px;
49
+ border-radius: 8px;
50
+ border: 1px solid var(--color-border-weak, light-dark(#ededed, #303030));
51
+ background-color: var(--color-bg-weak, light-dark(#f1f1f1, #141414));
52
+
53
+ button {
54
+ appearance: none;
55
+ font-family: inherit;
56
+ font-size: 13px;
57
+ color: var(--color-fg-default, light-dark(#111111, #ffffff));
58
+ border: 1px solid transparent;
59
+ border-radius: 6px;
60
+ padding: 4px 10px;
61
+ background-color: transparent;
62
+ cursor: pointer;
63
+ }
64
+
65
+ button:hover {
66
+ background-color: var(--color-bg-action-neutral-default-hover, light-dark(#c6c6c6, #565656));
67
+ }
68
+
69
+ button[aria-pressed="true"] {
70
+ border-color: var(--color-border-action-neutral-default, #999999);
71
+ background-color: var(--color-bg-action-neutral-default, light-dark(#d4d4d4, #414141));
72
+ }
73
+ }
74
+ `;
75
+
76
+ function initializeThemeSwitcher() {
77
+ const validSchemes = COLOR_SCHEMES.map(([scheme]) => scheme);
78
+ const embedded = window.self !== window.top;
79
+
80
+ const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
81
+ const applyOsPreference = () => document.body.classList.toggle("prefers-dark", prefersDark.matches);
82
+ prefersDark.addEventListener("change", applyOsPreference);
83
+ applyOsPreference();
84
+
85
+ const buttons = new Map();
86
+ const setPreference = (preference) => {
87
+ document.body.setAttribute("sd-color-scheme", preference);
88
+ buttons.forEach((button, scheme) => button.setAttribute("aria-pressed", String(scheme === preference)));
89
+ try {
90
+ localStorage.setItem(STORAGE_KEY, preference);
91
+ } catch {
92
+ // storage unavailable (e.g. private mode) — the preference just doesn't persist
93
+ }
94
+ window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: preference }));
95
+ };
96
+
97
+ // Host pages control embedded pages through messages.
98
+ window.addEventListener("message", (event) => {
99
+ if (event.data?.type === MESSAGE_TYPE && validSchemes.includes(event.data.preference)) {
100
+ setPreference(event.data.preference);
101
+ }
102
+ });
103
+
104
+ if (!embedded) {
105
+ const styles = document.createElement("style");
106
+ styles.textContent = switcherStyle;
107
+ document.head.appendChild(styles);
108
+
109
+ const switcher = document.createElement("div");
110
+ switcher.className = "theme-switcher";
111
+ switcher.role = "group";
112
+ switcher.ariaLabel = "Color scheme";
113
+ for (const [scheme, label] of COLOR_SCHEMES) {
114
+ const button = document.createElement("button");
115
+ button.type = "button";
116
+ button.innerText = label;
117
+ button.addEventListener("click", () => setPreference(scheme));
118
+ buttons.set(scheme, button);
119
+ switcher.appendChild(button);
120
+ }
121
+ document.body.appendChild(switcher);
122
+ }
123
+
124
+ let storedPreference = null;
125
+ try {
126
+ storedPreference = localStorage.getItem(STORAGE_KEY);
127
+ } catch {
128
+ // storage unavailable — fall back to auto
129
+ }
130
+ setPreference(validSchemes.includes(storedPreference) ? storedPreference : "light dark");
131
+ }
132
+
133
+ initializeThemeSwitcher();