starlight-theme-bejamas 0.1.18 → 0.2.3
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/CHANGELOG.md +32 -0
- package/package.json +8 -7
- package/src/config.test.ts +40 -0
- package/src/config.ts +4 -3
- package/src/global.d.ts +4 -2
- package/src/index.ts +0 -7
- package/src/lib/theme-choice.test.ts +251 -0
- package/src/lib/theme-choice.ts +346 -0
- package/src/lib/theme-stylesheet-runtime.test.ts +246 -0
- package/src/lib/theme-stylesheet.test.ts +16 -0
- package/src/lib/theme-stylesheet.ts +123 -0
- package/src/overrides/MobileTableOfContents.astro +6 -6
- package/src/overrides/PageFrame.astro +40 -83
- package/src/overrides/PageTitle.astro +2 -2
- package/src/overrides/TableOfContents/TableOfContentsList.astro +1 -1
- package/src/overrides/ThemeProvider.astro +101 -31
- package/src/overrides/ThemeSelect.astro +32 -47
- package/src/styles/theme.css +42 -36
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
1
|
# starlight-theme-bejamas
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [[`d8abe23`](https://github.com/bejamas/ui/commit/d8abe237f179cc6a5be73823a134a6221c62cca5)]:
|
|
8
|
+
- @bejamas/semantic-icons@0.2.0
|
|
9
|
+
|
|
10
|
+
## 0.2.2
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- [#159](https://github.com/bejamas/ui/pull/159) [`d9fb503`](https://github.com/bejamas/ui/commit/d9fb50331b2692cad08f1653611b952312e6d7fe) Thanks [@Tadzik-2](https://github.com/Tadzik-2)! - Point sidebar group carets down when collapsed and up when expanded.
|
|
15
|
+
|
|
16
|
+
## 0.2.1
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#143](https://github.com/bejamas/ui/pull/143) [`94b6e69`](https://github.com/bejamas/ui/commit/94b6e698d22920f974d263709828cb9cfaa1bce5) Thanks [@Tadzik-2](https://github.com/Tadzik-2)! - Publish semantic icons publicly and validate packed release manifests so the theme installs without workspace protocol or private runtime dependencies.
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`94b6e69`](https://github.com/bejamas/ui/commit/94b6e698d22920f974d263709828cb9cfaa1bce5)]:
|
|
23
|
+
- @bejamas/semantic-icons@0.1.1
|
|
24
|
+
|
|
25
|
+
## 0.2.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- [#102](https://github.com/bejamas/ui/pull/102) [`25987b8`](https://github.com/bejamas/ui/commit/25987b872d38280dfe47f820019f86c1cb00eb6d) Thanks [@thomkrupa](https://github.com/thomkrupa)! - Improve the Starlight Bejamas theme runtime and expose the shared theme-choice helpers for consumers that need to integrate with theme state directly.
|
|
30
|
+
- Unify light, dark, and auto theme choice handling across the theme picker, page chrome, and preview surfaces so theme state stays in sync more reliably.
|
|
31
|
+
- Refresh browser theme-color metadata and the active theme stylesheet more consistently when theme state or presets change.
|
|
32
|
+
- Sync semantic icon updates as part of the theme runtime so icon-library changes propagate cleanly with preset-driven theme updates.
|
|
33
|
+
- Export `starlight-theme-bejamas/lib/theme-choice` as a public entrypoint for consumers that want to read, apply, or react to the shared theme-choice state.
|
|
34
|
+
|
|
3
35
|
## 0.1.18
|
|
4
36
|
|
|
5
37
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starlight-theme-bejamas",
|
|
3
3
|
"author": "Bejamas",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.3",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "A Starlight theme using bejamas/ui",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/index.ts",
|
|
10
10
|
"./config": "./src/config.ts",
|
|
11
|
+
"./lib/theme-choice": "./src/lib/theme-choice.ts",
|
|
11
12
|
"./styles/theme.css": "./src/styles/theme.css",
|
|
12
13
|
"./overrides/Header.astro": "./src/overrides/Header.astro",
|
|
13
14
|
"./overrides/PageFrame.astro": "./src/overrides/PageFrame.astro",
|
|
@@ -37,13 +38,13 @@
|
|
|
37
38
|
},
|
|
38
39
|
"bugs": "https://github.com/bejamas/ui/issues",
|
|
39
40
|
"peerDependencies": {
|
|
40
|
-
"@astrojs/starlight": "
|
|
41
|
-
"astro": "
|
|
41
|
+
"@astrojs/starlight": "^0.42.3",
|
|
42
|
+
"astro": "^7.3.4"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@
|
|
45
|
-
"@fontsource-variable/inter": "^5.
|
|
46
|
-
"@fontsource/inter": "^5.
|
|
47
|
-
"@lucide/astro": "^
|
|
45
|
+
"@bejamas/semantic-icons": "0.2.0",
|
|
46
|
+
"@fontsource-variable/inter": "^5.3.0",
|
|
47
|
+
"@fontsource/inter": "^5.3.0",
|
|
48
|
+
"@lucide/astro": "^1.47.0"
|
|
48
49
|
}
|
|
49
50
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { StarlightThemeBejamasConfigSchema } from "./config";
|
|
4
|
+
|
|
5
|
+
describe("starlight-theme-bejamas config schema", () => {
|
|
6
|
+
test("parses nav and component maps under zod v4", () => {
|
|
7
|
+
const result = StarlightThemeBejamasConfigSchema.safeParse({
|
|
8
|
+
nav: [
|
|
9
|
+
{
|
|
10
|
+
label: {
|
|
11
|
+
en: "Docs",
|
|
12
|
+
fr: "Documentation",
|
|
13
|
+
},
|
|
14
|
+
href: "/docs/introduction",
|
|
15
|
+
attrs: {
|
|
16
|
+
rel: "prefetch",
|
|
17
|
+
"data-track": "docs-link",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
components: {
|
|
22
|
+
ThemeSelect: "./src/components/ThemeSwitcher.astro",
|
|
23
|
+
PageTitle: "./src/components/PageTitle.astro",
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(result.success).toBe(true);
|
|
28
|
+
if (!result.success) {
|
|
29
|
+
throw result.error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
expect(result.data.nav?.[0]?.label).toEqual({
|
|
33
|
+
en: "Docs",
|
|
34
|
+
fr: "Documentation",
|
|
35
|
+
});
|
|
36
|
+
expect(result.data.components?.ThemeSelect).toBe(
|
|
37
|
+
"./src/components/ThemeSwitcher.astro",
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
package/src/config.ts
CHANGED
|
@@ -3,7 +3,8 @@ import type { HTMLAttributes } from "astro/types";
|
|
|
3
3
|
import { z } from "astro/zod";
|
|
4
4
|
|
|
5
5
|
const linkHTMLAttributesSchema = z.record(
|
|
6
|
-
z.
|
|
6
|
+
z.string(),
|
|
7
|
+
z.union([z.string(), z.number(), z.boolean(), z.undefined()]),
|
|
7
8
|
) as z.Schema<
|
|
8
9
|
Omit<HTMLAttributes<"a">, keyof AstroBuiltinAttributes | "children">
|
|
9
10
|
>;
|
|
@@ -25,7 +26,7 @@ const navLinkSchema = z.object({
|
|
|
25
26
|
* The value can be a string, or for multilingual sites, an object with values for each different locale. When using
|
|
26
27
|
* the object form, the keys must be BCP-47 tags (e.g. en, fr, or zh-CN).
|
|
27
28
|
*/
|
|
28
|
-
label: z.union([z.string(), z.record(z.string())]),
|
|
29
|
+
label: z.union([z.string(), z.record(z.string(), z.string())]),
|
|
29
30
|
/**
|
|
30
31
|
* The link to the topic’s content which an be a relative link to local files or the full URL of an external page.
|
|
31
32
|
*
|
|
@@ -39,7 +40,7 @@ const navLinkSchema = z.object({
|
|
|
39
40
|
|
|
40
41
|
export const StarlightThemeBejamasConfigSchema = z.object({
|
|
41
42
|
nav: z.array(navLinkSchema).optional(),
|
|
42
|
-
components: z.record(z.string()).optional(),
|
|
43
|
+
components: z.record(z.string(), z.string()).optional(),
|
|
43
44
|
});
|
|
44
45
|
|
|
45
46
|
export type StarlightThemeBejamasUserConfig = z.input<
|
package/src/global.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
declare global {
|
|
2
2
|
interface Window {
|
|
3
3
|
StarlightThemeProvider?: {
|
|
4
|
-
|
|
4
|
+
refreshThemeColors?: (theme?: string) => void;
|
|
5
|
+
updatePickers?: (theme?: string) => void;
|
|
5
6
|
};
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
const StarlightThemeProvider: {
|
|
9
|
-
|
|
10
|
+
refreshThemeColors?: (theme?: string) => void;
|
|
11
|
+
updatePickers?: (theme?: string) => void;
|
|
10
12
|
};
|
|
11
13
|
|
|
12
14
|
namespace App {
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
type StarlightThemeBejamasUserConfig,
|
|
12
12
|
} from "./config";
|
|
13
13
|
import { vitePluginStarlightThemeBejamas } from "./lib/vite";
|
|
14
|
-
// import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
|
|
15
14
|
|
|
16
15
|
export default function starlightThemeBejamas(
|
|
17
16
|
userConfig: StarlightThemeBejamasUserConfig,
|
|
@@ -48,12 +47,6 @@ export default function starlightThemeBejamas(
|
|
|
48
47
|
? {}
|
|
49
48
|
: starlightConfig.expressiveCode;
|
|
50
49
|
|
|
51
|
-
// starlightConfig.plugins?.push(
|
|
52
|
-
// pluginLineNumbers({
|
|
53
|
-
// showLineNumbers: true,
|
|
54
|
-
// }),
|
|
55
|
-
// );
|
|
56
|
-
|
|
57
50
|
const componentOverrides: typeof config.components = {};
|
|
58
51
|
|
|
59
52
|
type OverridableComponent =
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
STARLIGHT_THEME_STORAGE_KEY,
|
|
4
|
+
THEME_TOGGLE_CHANGED_EVENT,
|
|
5
|
+
applyThemeModeToRoot,
|
|
6
|
+
buildThemeChoiceBootstrapInlineScript,
|
|
7
|
+
getThemeChoiceFromRoot,
|
|
8
|
+
getThemeModeFromRoot,
|
|
9
|
+
loadStoredThemeChoice,
|
|
10
|
+
parseStoredThemeChoice,
|
|
11
|
+
resolveThemeMode,
|
|
12
|
+
setThemeChoice,
|
|
13
|
+
storeThemeChoice,
|
|
14
|
+
syncThemeChoiceControls,
|
|
15
|
+
} from "./theme-choice";
|
|
16
|
+
|
|
17
|
+
function createThemeRoot() {
|
|
18
|
+
const classes = new Set<string>();
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
classes,
|
|
22
|
+
root: {
|
|
23
|
+
dataset: {} as Record<string, string | undefined>,
|
|
24
|
+
classList: {
|
|
25
|
+
add: (...tokens: string[]) => {
|
|
26
|
+
for (const token of tokens) {
|
|
27
|
+
classes.add(token);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
remove: (...tokens: string[]) => {
|
|
31
|
+
for (const token of tokens) {
|
|
32
|
+
classes.delete(token);
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
contains: (token: string) => classes.has(token),
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe("theme choice runtime", () => {
|
|
42
|
+
it("parses only supported stored theme choices", () => {
|
|
43
|
+
expect(parseStoredThemeChoice("light")).toBe("light");
|
|
44
|
+
expect(parseStoredThemeChoice("dark")).toBe("dark");
|
|
45
|
+
expect(parseStoredThemeChoice("auto")).toBe("auto");
|
|
46
|
+
expect(parseStoredThemeChoice("")).toBe("auto");
|
|
47
|
+
expect(parseStoredThemeChoice("system")).toBe("auto");
|
|
48
|
+
expect(parseStoredThemeChoice(undefined)).toBe("auto");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("loads the stored theme choice from localStorage-compatible storage", () => {
|
|
52
|
+
expect(
|
|
53
|
+
loadStoredThemeChoice({
|
|
54
|
+
getItem: (key) =>
|
|
55
|
+
key === STARLIGHT_THEME_STORAGE_KEY ? "dark" : null,
|
|
56
|
+
}),
|
|
57
|
+
).toBe("dark");
|
|
58
|
+
|
|
59
|
+
expect(
|
|
60
|
+
loadStoredThemeChoice({
|
|
61
|
+
getItem: () => "unexpected",
|
|
62
|
+
}),
|
|
63
|
+
).toBe("auto");
|
|
64
|
+
|
|
65
|
+
expect(
|
|
66
|
+
loadStoredThemeChoice({
|
|
67
|
+
getItem: () => {
|
|
68
|
+
throw new Error("storage unavailable");
|
|
69
|
+
},
|
|
70
|
+
}),
|
|
71
|
+
).toBe("auto");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("resolves auto against the preferred color scheme", () => {
|
|
75
|
+
expect(resolveThemeMode("light", false)).toBe("light");
|
|
76
|
+
expect(resolveThemeMode("dark", true)).toBe("dark");
|
|
77
|
+
expect(resolveThemeMode("auto", true)).toBe("light");
|
|
78
|
+
expect(resolveThemeMode("auto", false)).toBe("dark");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("applies theme attributes and class state to the target root", () => {
|
|
82
|
+
const { root, classes } = createThemeRoot();
|
|
83
|
+
|
|
84
|
+
classes.add("dark");
|
|
85
|
+
applyThemeModeToRoot(root, "auto", "light");
|
|
86
|
+
|
|
87
|
+
expect(root.dataset.theme).toBe("light");
|
|
88
|
+
expect(root.dataset.themeChoice).toBe("auto");
|
|
89
|
+
expect(classes.has("light")).toBe(true);
|
|
90
|
+
expect(classes.has("dark")).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("stores explicit theme choices and clears auto to the empty localStorage value", () => {
|
|
94
|
+
const stored = new Map<string, string>();
|
|
95
|
+
const storage = {
|
|
96
|
+
setItem: (key: string, value: string) => {
|
|
97
|
+
stored.set(key, value);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
storeThemeChoice(storage, "dark");
|
|
102
|
+
storeThemeChoice(storage, "auto");
|
|
103
|
+
|
|
104
|
+
expect(stored.get(STARLIGHT_THEME_STORAGE_KEY)).toBe("");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("reads theme choice and effective mode from the document root", () => {
|
|
108
|
+
const { root, classes } = createThemeRoot();
|
|
109
|
+
|
|
110
|
+
root.dataset.themeChoice = "auto";
|
|
111
|
+
root.dataset.theme = "";
|
|
112
|
+
classes.add("dark");
|
|
113
|
+
|
|
114
|
+
expect(getThemeChoiceFromRoot(root)).toBe("auto");
|
|
115
|
+
expect(getThemeModeFromRoot(root, "light")).toBe("dark");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("syncs tabs-based and legacy select-based controls to the requested choice", () => {
|
|
119
|
+
let tabsValue = "";
|
|
120
|
+
let selectValue = "";
|
|
121
|
+
const nativeSelect = { value: "" };
|
|
122
|
+
const tabsRoot = {
|
|
123
|
+
dataset: {} as Record<string, string | undefined>,
|
|
124
|
+
dispatchEvent: (event: Event) => {
|
|
125
|
+
tabsValue =
|
|
126
|
+
(event as CustomEvent<{ value?: string }>).detail?.value ?? "";
|
|
127
|
+
return true;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
const selectRoot = {
|
|
131
|
+
dataset: {} as Record<string, string | undefined>,
|
|
132
|
+
dispatchEvent: (event: Event) => {
|
|
133
|
+
selectValue =
|
|
134
|
+
(event as CustomEvent<{ value?: string }>).detail?.value ?? "";
|
|
135
|
+
return true;
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
const picker = {
|
|
139
|
+
querySelector: (selector: string) => {
|
|
140
|
+
if (selector === '[data-slot="tabs"][data-theme-choice-tabs]') {
|
|
141
|
+
return tabsRoot;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (selector === "select") {
|
|
145
|
+
return nativeSelect;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (selector === '#starlight-theme-select, [data-slot="select"]') {
|
|
149
|
+
return selectRoot;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return null;
|
|
153
|
+
},
|
|
154
|
+
querySelectorAll: () => [] as unknown as NodeListOf<Element>,
|
|
155
|
+
};
|
|
156
|
+
const root = {
|
|
157
|
+
querySelectorAll: (selector: string) => {
|
|
158
|
+
if (selector === "starlight-theme-select") {
|
|
159
|
+
return [picker] as unknown as NodeListOf<Element>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (selector === '[data-slot="tabs"][data-theme-choice-tabs]') {
|
|
163
|
+
return [tabsRoot] as unknown as NodeListOf<Element>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return [] as unknown as NodeListOf<Element>;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
syncThemeChoiceControls("light", root as unknown as ParentNode);
|
|
171
|
+
|
|
172
|
+
expect(tabsRoot.dataset.defaultValue).toBe("light");
|
|
173
|
+
expect(tabsRoot.dataset.value).toBe("light");
|
|
174
|
+
expect(tabsValue).toBe("light");
|
|
175
|
+
expect(nativeSelect.value).toBe("light");
|
|
176
|
+
expect(selectRoot.dataset.defaultValue).toBe("light");
|
|
177
|
+
expect(selectRoot.dataset.value).toBe("light");
|
|
178
|
+
expect(selectValue).toBe("light");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("applies the shared global theme choice path including storage, controls, and event dispatch", () => {
|
|
182
|
+
const { root, classes } = createThemeRoot();
|
|
183
|
+
const stored = new Map<string, string>();
|
|
184
|
+
const dispatched: Array<{ theme: string; themeChoice: string }> = [];
|
|
185
|
+
let tabsValue = "";
|
|
186
|
+
const tabsRoot = {
|
|
187
|
+
dataset: {} as Record<string, string | undefined>,
|
|
188
|
+
dispatchEvent: (event: Event) => {
|
|
189
|
+
tabsValue =
|
|
190
|
+
(event as CustomEvent<{ value?: string }>).detail?.value ?? "";
|
|
191
|
+
return true;
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
const controlsRoot = {
|
|
195
|
+
querySelectorAll: (selector: string) => {
|
|
196
|
+
if (selector === '[data-slot="tabs"][data-theme-choice-tabs]') {
|
|
197
|
+
return [tabsRoot] as unknown as NodeListOf<Element>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return [] as unknown as NodeListOf<Element>;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
const dispatchTarget = {
|
|
204
|
+
dispatchEvent: (event: Event) => {
|
|
205
|
+
const customEvent = event as CustomEvent<{
|
|
206
|
+
theme?: string;
|
|
207
|
+
themeChoice?: string;
|
|
208
|
+
}>;
|
|
209
|
+
|
|
210
|
+
if (event.type === THEME_TOGGLE_CHANGED_EVENT) {
|
|
211
|
+
dispatched.push({
|
|
212
|
+
theme: customEvent.detail?.theme ?? "",
|
|
213
|
+
themeChoice: customEvent.detail?.themeChoice ?? "",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return true;
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const effectiveTheme = setThemeChoice("dark", {
|
|
222
|
+
root,
|
|
223
|
+
prefersLight: true,
|
|
224
|
+
storage: {
|
|
225
|
+
setItem: (key, value) => {
|
|
226
|
+
stored.set(key, value);
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
controlsRoot: controlsRoot as unknown as ParentNode,
|
|
230
|
+
dispatchTarget: dispatchTarget as unknown as EventTarget,
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
expect(effectiveTheme).toBe("dark");
|
|
234
|
+
expect(root.dataset.themeChoice).toBe("dark");
|
|
235
|
+
expect(root.dataset.theme).toBe("dark");
|
|
236
|
+
expect(classes.has("dark")).toBe(true);
|
|
237
|
+
expect(stored.get(STARLIGHT_THEME_STORAGE_KEY)).toBe("dark");
|
|
238
|
+
expect(tabsValue).toBe("dark");
|
|
239
|
+
expect(dispatched).toEqual([{ theme: "dark", themeChoice: "dark" }]);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("builds an inline bootstrap script that owns root state and control sync", () => {
|
|
243
|
+
const script = buildThemeChoiceBootstrapInlineScript();
|
|
244
|
+
|
|
245
|
+
expect(script).toContain(STARLIGHT_THEME_STORAGE_KEY);
|
|
246
|
+
expect(script).toContain("dataset.themeChoice");
|
|
247
|
+
expect(script).toContain('[data-slot="tabs"][data-theme-choice-tabs]');
|
|
248
|
+
expect(script).toContain(THEME_TOGGLE_CHANGED_EVENT);
|
|
249
|
+
expect(script).toContain("prefers-color-scheme: light");
|
|
250
|
+
});
|
|
251
|
+
});
|