@cas-smartdesign/design-tokens 3.0.3 → 3.0.4
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/docs/index.html +157 -93
- package/package.json +8 -2
- package/theme-switcher.d.ts +3 -0
- package/theme-switcher.mjs +133 -0
package/dist/docs/index.html
CHANGED
|
@@ -12,18 +12,29 @@
|
|
|
12
12
|
box-sizing: border-box;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
html {
|
|
16
|
+
height: 100%;
|
|
17
|
+
}
|
|
18
|
+
|
|
15
19
|
body {
|
|
16
20
|
font: var(--text-body);
|
|
17
21
|
color: var(--color-fg-default);
|
|
18
22
|
background-color: var(--color-bg-default);
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
/* The page itself does not scroll — the sidebar and the markdown content are
|
|
24
|
+
individual scroll containers, so anchor navigation only scrolls the content. */
|
|
25
|
+
height: 100%;
|
|
26
|
+
margin: 0;
|
|
27
|
+
overflow: hidden;
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
.markdown-body {
|
|
24
31
|
box-sizing: border-box;
|
|
25
32
|
padding-bottom: var(--spacing-x16);
|
|
26
33
|
|
|
34
|
+
overflow-y: auto;
|
|
35
|
+
min-width: 0;
|
|
36
|
+
scroll-behavior: smooth;
|
|
37
|
+
|
|
27
38
|
color: inherit !important;
|
|
28
39
|
background-color: inherit !important;
|
|
29
40
|
|
|
@@ -37,6 +48,7 @@ body {
|
|
|
37
48
|
& svg {
|
|
38
49
|
float: none;
|
|
39
50
|
margin: 0;
|
|
51
|
+
max-width: 100%;
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
54
|
|
|
@@ -141,40 +153,6 @@ body {
|
|
|
141
153
|
.specimen-meta code {
|
|
142
154
|
white-space: pre;
|
|
143
155
|
}
|
|
144
|
-
.theme-container {
|
|
145
|
-
background-color: var(--color-bg-default);
|
|
146
|
-
top: var(--spacing-x16);
|
|
147
|
-
right: var(--spacing-x16);
|
|
148
|
-
width: auto;
|
|
149
|
-
padding: var(--spacing-x4);
|
|
150
|
-
overflow: hidden;
|
|
151
|
-
border-radius: var(--radius-m);
|
|
152
|
-
}
|
|
153
|
-
.theme-toggle-container {
|
|
154
|
-
display: flex;
|
|
155
|
-
justify-content: center;
|
|
156
|
-
gap: var(--spacing-x4);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.theme-toggle {
|
|
160
|
-
appearance: none;
|
|
161
|
-
border-radius: var(--radius-s);
|
|
162
|
-
min-width: 80px;
|
|
163
|
-
height: 36px;
|
|
164
|
-
color: var(--color-fg-default);
|
|
165
|
-
border: 1px solid var(--color-border-action-neutral-default);
|
|
166
|
-
padding: var(--spacing-x2) var(--spacing-x4);
|
|
167
|
-
background: var(--color-bg-action-neutral-default);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
.theme-toggle:focus,
|
|
171
|
-
.theme-toggle:hover {
|
|
172
|
-
background: var(--color-bg-action-neutral-default-hover);
|
|
173
|
-
}
|
|
174
|
-
.theme-toggle:active {
|
|
175
|
-
background: var(--color-bg-action-neutral-default-pressed);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
156
|
.hidden {
|
|
179
157
|
display: none;
|
|
180
158
|
}
|
|
@@ -220,6 +198,7 @@ body {
|
|
|
220
198
|
position: relative;
|
|
221
199
|
display: flex;
|
|
222
200
|
flex-direction: row;
|
|
201
|
+
height: 100%;
|
|
223
202
|
& > * {
|
|
224
203
|
flex: 1;
|
|
225
204
|
}
|
|
@@ -227,6 +206,7 @@ body {
|
|
|
227
206
|
|
|
228
207
|
.sidebar {
|
|
229
208
|
flex: 0 0 auto;
|
|
209
|
+
overflow-y: auto;
|
|
230
210
|
padding-right: var(--spacing-x8);
|
|
231
211
|
margin-right: var(--spacing-x16);
|
|
232
212
|
border-right: 1px solid var(--color-border-default);
|
|
@@ -1394,16 +1374,7 @@ body {
|
|
|
1394
1374
|
</head>
|
|
1395
1375
|
<body sd-color-scheme="light dark">
|
|
1396
1376
|
<div class="content-wrapper">
|
|
1397
|
-
<div class="sidebar">
|
|
1398
|
-
<div class="theme-container">
|
|
1399
|
-
<div class="theme-toggle-container">
|
|
1400
|
-
<button id="light-theme-toggle" class="theme-toggle">☀️ light</button>
|
|
1401
|
-
<button id="dark-theme-toggle" class="theme-toggle">🌙 dark</button>
|
|
1402
|
-
<button id="auto-theme-toggle" class="theme-toggle">🌓 auto</button>
|
|
1403
|
-
</div>
|
|
1404
|
-
</div>
|
|
1405
|
-
<ul class="nav-bar"><li class="level-2"><a href="#token-structure">Token structure</a></li><li class="level-3"><a href="#base-tokens">Base tokens</a></li><li class="level-3"><a href="#semantic-tokens">Semantic tokens</a></li><li class="level-3"><a href="#themes">Themes</a></li><li class="level-2"><a href="#semantic-tokens---color">Semantic tokens - Color</a></li><li class="level-3"><a href="#choose-the-type-(-where?-)">Choose the Type ( Where? )</a></li><li class="level-4"><a href="#fg">fg</a></li><li class="level-4"><a href="#bg">bg</a></li><li class="level-4"><a href="#border">border</a></li><li class="level-4"><a href="#rainbow">rainbow</a></li><li class="level-3"><a href="#choose-the-role-(-why?-)">Choose the Role ( Why? )</a></li><li class="level-4"><a href="#empty">empty</a></li><li class="level-4"><a href="#accent">accent</a></li><li class="level-4"><a href="#danger">danger</a></li><li class="level-4"><a href="#success">success</a></li><li class="level-4"><a href="#warning">warning</a></li><li class="level-4"><a href="#assistive">assistive</a></li><li class="level-4"><a href="#action">action</a></li><li class="level-4"><a href="#selection">selection</a></li><li class="level-4"><a href="#interaction">interaction</a></li><li class="level-3"><a href="#choose-the-prominence-(-how-important?-)">Choose the Prominence ( How important? )</a></li><li class="level-4"><a href="#oncolor">onColor</a></li><li class="level-4"><a href="#translucent-+-ontranslucent">Translucent + onTranslucent</a></li><li class="level-4"><a href="#stronger">stronger</a></li><li class="level-4"><a href="#strong">strong</a></li><li class="level-4"><a href="#default">default</a></li><li class="level-4"><a href="#weak">weak</a></li><li class="level-4"><a href="#weaker">weaker</a></li><li class="level-4"><a href="#brand">brand</a></li><li class="level-4"><a href="#accent">accent</a></li><li class="level-4"><a href="#neutral">neutral</a></li><li class="level-4"><a href="#dangerdefault">dangerDefault</a></li><li class="level-3"><a href="#choose-the-state-(-when?-)">Choose the State ( When? )</a></li><li class="level-4"><a href="#empty">empty</a></li><li class="level-4"><a href="#hover">hover</a></li><li class="level-4"><a href="#pressed">pressed</a></li><li class="level-4"><a href="#disabled">disabled</a></li><li class="level-4"><a href="#focus">focus</a></li><li class="level-4"><a href="#oncolor">onColor</a></li><li class="level-2"><a href="#semantic-tokens---typography">Semantic tokens - Typography</a></li><li class="level-3"><a href="#displaylarge">displayLarge</a></li><li class="level-3"><a href="#displaymedium">displayMedium</a></li><li class="level-3"><a href="#displaysmall">displaySmall</a></li><li class="level-3"><a href="#titlelarge">titleLarge</a></li><li class="level-3"><a href="#titlemedium">titleMedium</a></li><li class="level-3"><a href="#titlesmall">titleSmall</a></li><li class="level-3"><a href="#subtitle">subtitle</a></li><li class="level-3"><a href="#body">body</a></li><li class="level-3"><a href="#description">description</a></li><li class="level-3"><a href="#caption">caption</a></li><li class="level-2"><a href="#semantic-tokens---typography-variants">Semantic tokens - Typography variants</a></li><li class="level-3"><a href="#highlighted">highlighted</a></li><li class="level-3"><a href="#strong">strong</a></li><li class="level-3"><a href="#default">default</a></li><li class="level-3"><a href="#sub">sub</a></li><li class="level-2"><a href="#semantic-tokens---corner-radius">Semantic Tokens - Corner Radius</a></li><li class="level-3"><a href="#radius/none">radius/none</a></li><li class="level-3"><a href="#radius/xs">radius/xs</a></li><li class="level-3"><a href="#radius/s">radius/s</a></li><li class="level-3"><a href="#radius/m">radius/m</a></li><li class="level-3"><a href="#radius/xl">radius/xl</a></li><li class="level-3"><a href="#radius/round">radius/round</a></li><li class="level-2"><a href="#semantic-tokens---effects">Semantic Tokens - Effects</a></li><li class="level-3"><a href="#shadow-strong">shadow-strong</a></li><li class="level-3"><a href="#shadow-default">shadow-default</a></li><li class="level-3"><a href="#shadow-weak">shadow-weak</a></li><li class="level-2"><a href="#semantic-tokens---opacity">Semantic Tokens - Opacity</a></li><li class="level-2"><a href="#semantic-tokens---spacing">Semantic Tokens - Spacing</a></li><li class="level-2"><a href="#examples">Examples</a></li></ul>
|
|
1406
|
-
</div>
|
|
1377
|
+
<div class="sidebar"><ul class="nav-bar"><li class="level-2"><a href="#token-structure">Token structure</a></li><li class="level-3"><a href="#base-tokens">Base tokens</a></li><li class="level-3"><a href="#semantic-tokens">Semantic tokens</a></li><li class="level-3"><a href="#themes">Themes</a></li><li class="level-2"><a href="#semantic-tokens---color">Semantic tokens - Color</a></li><li class="level-3"><a href="#choose-the-type-(-where?-)">Choose the Type ( Where? )</a></li><li class="level-4"><a href="#fg">fg</a></li><li class="level-4"><a href="#bg">bg</a></li><li class="level-4"><a href="#border">border</a></li><li class="level-4"><a href="#rainbow">rainbow</a></li><li class="level-3"><a href="#choose-the-role-(-why?-)">Choose the Role ( Why? )</a></li><li class="level-4"><a href="#empty">empty</a></li><li class="level-4"><a href="#accent">accent</a></li><li class="level-4"><a href="#danger">danger</a></li><li class="level-4"><a href="#success">success</a></li><li class="level-4"><a href="#warning">warning</a></li><li class="level-4"><a href="#assistive">assistive</a></li><li class="level-4"><a href="#action">action</a></li><li class="level-4"><a href="#selection">selection</a></li><li class="level-4"><a href="#interaction">interaction</a></li><li class="level-3"><a href="#choose-the-prominence-(-how-important?-)">Choose the Prominence ( How important? )</a></li><li class="level-4"><a href="#oncolor">onColor</a></li><li class="level-4"><a href="#translucent-+-ontranslucent">Translucent + onTranslucent</a></li><li class="level-4"><a href="#stronger">stronger</a></li><li class="level-4"><a href="#strong">strong</a></li><li class="level-4"><a href="#default">default</a></li><li class="level-4"><a href="#weak">weak</a></li><li class="level-4"><a href="#weaker">weaker</a></li><li class="level-4"><a href="#brand">brand</a></li><li class="level-4"><a href="#accent">accent</a></li><li class="level-4"><a href="#neutral">neutral</a></li><li class="level-4"><a href="#dangerdefault">dangerDefault</a></li><li class="level-3"><a href="#choose-the-state-(-when?-)">Choose the State ( When? )</a></li><li class="level-4"><a href="#empty">empty</a></li><li class="level-4"><a href="#hover">hover</a></li><li class="level-4"><a href="#pressed">pressed</a></li><li class="level-4"><a href="#disabled">disabled</a></li><li class="level-4"><a href="#focus">focus</a></li><li class="level-4"><a href="#oncolor">onColor</a></li><li class="level-2"><a href="#semantic-tokens---typography">Semantic tokens - Typography</a></li><li class="level-3"><a href="#displaylarge">displayLarge</a></li><li class="level-3"><a href="#displaymedium">displayMedium</a></li><li class="level-3"><a href="#displaysmall">displaySmall</a></li><li class="level-3"><a href="#titlelarge">titleLarge</a></li><li class="level-3"><a href="#titlemedium">titleMedium</a></li><li class="level-3"><a href="#titlesmall">titleSmall</a></li><li class="level-3"><a href="#subtitle">subtitle</a></li><li class="level-3"><a href="#body">body</a></li><li class="level-3"><a href="#description">description</a></li><li class="level-3"><a href="#caption">caption</a></li><li class="level-2"><a href="#semantic-tokens---typography-variants">Semantic tokens - Typography variants</a></li><li class="level-3"><a href="#highlighted">highlighted</a></li><li class="level-3"><a href="#strong">strong</a></li><li class="level-3"><a href="#default">default</a></li><li class="level-3"><a href="#sub">sub</a></li><li class="level-2"><a href="#semantic-tokens---corner-radius">Semantic Tokens - Corner Radius</a></li><li class="level-3"><a href="#radius/none">radius/none</a></li><li class="level-3"><a href="#radius/xs">radius/xs</a></li><li class="level-3"><a href="#radius/s">radius/s</a></li><li class="level-3"><a href="#radius/m">radius/m</a></li><li class="level-3"><a href="#radius/xl">radius/xl</a></li><li class="level-3"><a href="#radius/round">radius/round</a></li><li class="level-2"><a href="#semantic-tokens---effects">Semantic Tokens - Effects</a></li><li class="level-3"><a href="#shadow-strong">shadow-strong</a></li><li class="level-3"><a href="#shadow-default">shadow-default</a></li><li class="level-3"><a href="#shadow-weak">shadow-weak</a></li><li class="level-2"><a href="#semantic-tokens---opacity">Semantic Tokens - Opacity</a></li><li class="level-2"><a href="#semantic-tokens---spacing">Semantic Tokens - Spacing</a></li><li class="level-2"><a href="#examples">Examples</a></li></ul></div>
|
|
1407
1378
|
<div class="markdown-body">
|
|
1408
1379
|
<div id="markdown-container"><h1 id="@cas-smartdesign/design-tokens">@cas-smartdesign/design-tokens</h1>
|
|
1409
1380
|
<p>A shared package providing design tokens to create SmartDesign like components and apps.</p>
|
|
@@ -6410,62 +6381,155 @@ These sizes are proportional to one another.</p>
|
|
|
6410
6381
|
</svg>
|
|
6411
6382
|
</p>
|
|
6412
6383
|
</div>
|
|
6384
|
+
<!-- Fallback for token categories without a readme section — inside the
|
|
6385
|
+
scroll container, as the page itself does not scroll. -->
|
|
6386
|
+
<div id="token-container"></div>
|
|
6413
6387
|
</div>
|
|
6414
6388
|
</div>
|
|
6415
6389
|
|
|
6416
|
-
<
|
|
6390
|
+
<script type="module">
|
|
6391
|
+
/**
|
|
6392
|
+
* Light / dark / auto switcher for the SmartDesign color-scheme protocol.
|
|
6393
|
+
*
|
|
6394
|
+
* Importing this module applies the persisted preference and renders a fixed switcher in
|
|
6395
|
+
* the top-right corner of the page:
|
|
6396
|
+
*
|
|
6397
|
+
* - the `sd-color-scheme` attribute on <body> holds the preference
|
|
6398
|
+
* ("light" | "dark" | "light dark" for auto) — the selectors of the generated token CSS
|
|
6399
|
+
* key off it (see build.mts),
|
|
6400
|
+
* - the `prefers-dark` class on <body> mirrors the OS preference,
|
|
6401
|
+
* - the preference is persisted under the "sd-color-scheme" localStorage key.
|
|
6402
|
+
*
|
|
6403
|
+
* Embedding: when the page runs inside an iframe no UI is rendered — the host page owns
|
|
6404
|
+
* the switcher and controls the embedded page by posting
|
|
6405
|
+
* `{ type: "sd-color-scheme", preference }` messages. Every applied preference change is
|
|
6406
|
+
* announced as a "sd-color-scheme-change" CustomEvent on window, which host pages (e.g.
|
|
6407
|
+
* the aggregated documentation index) use to forward the preference to embedded pages.
|
|
6408
|
+
*
|
|
6409
|
+
* Shipped as a hand-written, dependency-free ES module so it can be used without any
|
|
6410
|
+
* build step (plain <script type="module">, inlining, or bundlers alike).
|
|
6411
|
+
*
|
|
6412
|
+
* Styling: importing this module does NOT load the token CSS — that is the page's own
|
|
6413
|
+
* concern (the ./css export). The switcher styles therefore use the token custom
|
|
6414
|
+
* properties with light-dark() fallbacks, so the switcher stays usable on pages that do
|
|
6415
|
+
* not load the tokens (e.g. the aggregated documentation index).
|
|
6416
|
+
*/
|
|
6417
|
+
|
|
6418
|
+
const STORAGE_KEY = "sd-color-scheme";
|
|
6419
|
+
const MESSAGE_TYPE = "sd-color-scheme";
|
|
6420
|
+
const CHANGE_EVENT = "sd-color-scheme-change";
|
|
6421
|
+
const COLOR_SCHEMES = [
|
|
6422
|
+
["light", "☀️ light"],
|
|
6423
|
+
["dark", "🌙 dark"],
|
|
6424
|
+
["light dark", "🌓 auto"],
|
|
6425
|
+
];
|
|
6426
|
+
|
|
6427
|
+
/* The light-dark() fallbacks mirror the light/dark values of the referenced tokens
|
|
6428
|
+
(resolved from dist/{light,dark}/tokens.css) so pages without the token CSS render
|
|
6429
|
+
identically to pages with it — keep them in sync when the tokens change. */
|
|
6430
|
+
const switcherStyle = `
|
|
6431
|
+
.theme-switcher {
|
|
6432
|
+
position: fixed;
|
|
6433
|
+
top: 16px;
|
|
6434
|
+
right: 16px;
|
|
6435
|
+
z-index: 100;
|
|
6436
|
+
display: flex;
|
|
6437
|
+
gap: 4px;
|
|
6438
|
+
padding: 4px;
|
|
6439
|
+
border-radius: 8px;
|
|
6440
|
+
border: 1px solid var(--color-border-weak, light-dark(#ededed, #303030));
|
|
6441
|
+
background-color: var(--color-bg-weak, light-dark(#f1f1f1, #141414));
|
|
6442
|
+
|
|
6443
|
+
button {
|
|
6444
|
+
appearance: none;
|
|
6445
|
+
font-family: inherit;
|
|
6446
|
+
font-size: 13px;
|
|
6447
|
+
color: var(--color-fg-default, light-dark(#111111, #ffffff));
|
|
6448
|
+
border: 1px solid transparent;
|
|
6449
|
+
border-radius: 6px;
|
|
6450
|
+
padding: 4px 10px;
|
|
6451
|
+
background-color: transparent;
|
|
6452
|
+
cursor: pointer;
|
|
6453
|
+
}
|
|
6417
6454
|
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
const darkToggle = document.getElementById("dark-theme-toggle");
|
|
6422
|
-
const autoToggle = document.getElementById("auto-theme-toggle");
|
|
6423
|
-
|
|
6424
|
-
const persistPreference = (preference) => {
|
|
6425
|
-
try {
|
|
6426
|
-
localStorage.setItem("sd-color-scheme", preference ?? "light dark");
|
|
6427
|
-
} catch {
|
|
6428
|
-
console.warn("Failed to persist theme preference to localstorage");
|
|
6429
|
-
}
|
|
6430
|
-
};
|
|
6431
|
-
|
|
6432
|
-
const readPreference = () => {
|
|
6433
|
-
try {
|
|
6434
|
-
return localStorage.getItem("sd-color-scheme") ?? "light dark";
|
|
6435
|
-
} catch (error) {
|
|
6436
|
-
return "light dark";
|
|
6437
|
-
}
|
|
6438
|
-
};
|
|
6439
|
-
const setPreference = (scheme) => {
|
|
6440
|
-
container.setAttribute("sd-color-scheme", scheme);
|
|
6441
|
-
persistPreference(scheme);
|
|
6442
|
-
};
|
|
6443
|
-
|
|
6444
|
-
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
6445
|
-
const onColorSchemePreferenceChange = (dark) => {
|
|
6446
|
-
document.body.classList.toggle("prefers-dark", dark);
|
|
6447
|
-
};
|
|
6448
|
-
prefersDark.addEventListener("change", (event) => {
|
|
6449
|
-
onColorSchemePreferenceChange(event.matches);
|
|
6450
|
-
});
|
|
6451
|
-
onColorSchemePreferenceChange(prefersDark.matches);
|
|
6455
|
+
button:hover {
|
|
6456
|
+
background-color: var(--color-bg-action-neutral-default-hover, light-dark(#c6c6c6, #565656));
|
|
6457
|
+
}
|
|
6452
6458
|
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6459
|
+
button[aria-pressed="true"] {
|
|
6460
|
+
border-color: var(--color-border-action-neutral-default, #999999);
|
|
6461
|
+
background-color: var(--color-bg-action-neutral-default, light-dark(#d4d4d4, #414141));
|
|
6462
|
+
}
|
|
6463
|
+
}
|
|
6464
|
+
`;
|
|
6465
|
+
|
|
6466
|
+
function initializeThemeSwitcher() {
|
|
6467
|
+
const validSchemes = COLOR_SCHEMES.map(([scheme]) => scheme);
|
|
6468
|
+
const embedded = window.self !== window.top;
|
|
6469
|
+
|
|
6470
|
+
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
|
|
6471
|
+
const applyOsPreference = () => document.body.classList.toggle("prefers-dark", prefersDark.matches);
|
|
6472
|
+
prefersDark.addEventListener("change", applyOsPreference);
|
|
6473
|
+
applyOsPreference();
|
|
6474
|
+
|
|
6475
|
+
const buttons = new Map();
|
|
6476
|
+
const setPreference = (preference) => {
|
|
6477
|
+
document.body.setAttribute("sd-color-scheme", preference);
|
|
6478
|
+
buttons.forEach((button, scheme) => button.setAttribute("aria-pressed", String(scheme === preference)));
|
|
6479
|
+
try {
|
|
6480
|
+
localStorage.setItem(STORAGE_KEY, preference);
|
|
6481
|
+
} catch {
|
|
6482
|
+
// storage unavailable (e.g. private mode) — the preference just doesn't persist
|
|
6483
|
+
}
|
|
6484
|
+
window.dispatchEvent(new CustomEvent(CHANGE_EVENT, { detail: preference }));
|
|
6485
|
+
};
|
|
6486
|
+
|
|
6487
|
+
// Host pages control embedded pages through messages.
|
|
6488
|
+
window.addEventListener("message", (event) => {
|
|
6489
|
+
if (event.data?.type === MESSAGE_TYPE && validSchemes.includes(event.data.preference)) {
|
|
6490
|
+
setPreference(event.data.preference);
|
|
6491
|
+
}
|
|
6492
|
+
});
|
|
6493
|
+
|
|
6494
|
+
if (!embedded) {
|
|
6495
|
+
const styles = document.createElement("style");
|
|
6496
|
+
styles.textContent = switcherStyle;
|
|
6497
|
+
document.head.appendChild(styles);
|
|
6498
|
+
|
|
6499
|
+
const switcher = document.createElement("div");
|
|
6500
|
+
switcher.className = "theme-switcher";
|
|
6501
|
+
switcher.role = "group";
|
|
6502
|
+
switcher.ariaLabel = "Color scheme";
|
|
6503
|
+
for (const [scheme, label] of COLOR_SCHEMES) {
|
|
6504
|
+
const button = document.createElement("button");
|
|
6505
|
+
button.type = "button";
|
|
6506
|
+
button.innerText = label;
|
|
6507
|
+
button.addEventListener("click", () => setPreference(scheme));
|
|
6508
|
+
buttons.set(scheme, button);
|
|
6509
|
+
switcher.appendChild(button);
|
|
6510
|
+
}
|
|
6511
|
+
document.body.appendChild(switcher);
|
|
6512
|
+
}
|
|
6513
|
+
|
|
6514
|
+
let storedPreference = null;
|
|
6515
|
+
try {
|
|
6516
|
+
storedPreference = localStorage.getItem(STORAGE_KEY);
|
|
6517
|
+
} catch {
|
|
6518
|
+
// storage unavailable — fall back to auto
|
|
6519
|
+
}
|
|
6520
|
+
setPreference(validSchemes.includes(storedPreference) ? storedPreference : "light dark");
|
|
6521
|
+
}
|
|
6522
|
+
|
|
6523
|
+
initializeThemeSwitcher();
|
|
6524
|
+
|
|
6525
|
+
</script>
|
|
6526
|
+
<script>
|
|
6527
|
+
document.body.querySelectorAll(".accordion-button").forEach((el) => {
|
|
6528
|
+
el.addEventListener("click", () => {
|
|
6464
6529
|
el.nextElementSibling?.classList.toggle("hidden");
|
|
6465
6530
|
el.classList.toggle("active");
|
|
6466
6531
|
});
|
|
6467
6532
|
});
|
|
6468
|
-
setPreference(readPreference());
|
|
6469
6533
|
</script>
|
|
6470
6534
|
</body>
|
|
6471
6535
|
</html>
|
package/package.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cas-smartdesign/design-tokens",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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.
|
|
49
|
+
"@cas-smartdesign/license-generator": "^1.10.2"
|
|
44
50
|
},
|
|
45
51
|
"scripts": {
|
|
46
52
|
"version": "pnpm version",
|
|
@@ -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();
|