| 1 | --- |
| 2 | /** |
| 3 | * Pre-paint theme initializer + browser-chrome metadata. Mirrors |
| 4 | * Starlight's default ThemeProvider contract so its built-in |
| 5 | * `<ThemeSelect>` (and our marketing nav toggle) keep working. |
| 6 | * |
| 7 | * Reads `localStorage["starlight-theme"]` first, falls back to |
| 8 | * `prefers-color-scheme`, and writes `data-theme` on <html> before any |
| 9 | * paint to avoid a flash. Imported by both the Starlight head override |
| 10 | * and the marketing layout so navigating between docs and marketing |
| 11 | * pages doesn't drop the user's theme. |
| 12 | * |
| 13 | * Also emits the `color-scheme` and `theme-color` meta tags so iOS / |
| 14 | * Android paint the URL bar / status bar / bottom nav with the page |
| 15 | * background instead of falling back to the OS-default white in dark |
| 16 | * mode. |
| 17 | * |
| 18 | * Exposes `window.StarlightThemeProvider.updatePickers(theme)` so the |
| 19 | * `<starlight-theme-select>` web component can sync its UI when storage |
| 20 | * or system preference changes. |
| 21 | */ |
| 22 | --- |
| 23 | |
| 24 | <meta name="color-scheme" content="light dark" /> |
| 25 | {/* Cream parchment for light, deep walnut for dark — matches `--alc-bg` |
| 26 | in tokens.css. The browser picks based on `prefers-color-scheme` so |
| 27 | iOS Safari's URL bar / Android's status bar tint correctly even |
| 28 | before the inline script writes `data-theme`. */} |
| 29 | <meta |
| 30 | name="theme-color" |
| 31 | content="#f5efe3" |
| 32 | media="(prefers-color-scheme: light)" |
| 33 | /> |
| 34 | <meta |
| 35 | name="theme-color" |
| 36 | content="#14110d" |
| 37 | media="(prefers-color-scheme: dark)" |
| 38 | /> |
| 39 | |
| 40 | <script is:inline> |
| 41 | window.StarlightThemeProvider = (() => { |
| 42 | const storageKey = "starlight-theme"; |
| 43 | const read = () => { |
| 44 | try { |
| 45 | return localStorage.getItem(storageKey); |
| 46 | } catch { |
| 47 | return null; |
| 48 | } |
| 49 | }; |
| 50 | const resolve = (pref) => { |
| 51 | if (pref === "light" || pref === "dark") return pref; |
| 52 | return window.matchMedia("(prefers-color-scheme: dark)").matches |
| 53 | ? "dark" |
| 54 | : "light"; |
| 55 | }; |
| 56 | const apply = () => { |
| 57 | document.documentElement.dataset.theme = resolve(read() || "auto"); |
| 58 | }; |
| 59 | apply(); |
| 60 | |
| 61 | // Re-resolve when the system theme changes and the user is on "auto". |
| 62 | window |
| 63 | .matchMedia("(prefers-color-scheme: dark)") |
| 64 | .addEventListener("change", () => { |
| 65 | const stored = read(); |
| 66 | if (!stored || stored === "auto") apply(); |
| 67 | }); |
| 68 | |
| 69 | return { |
| 70 | updatePickers(theme = read() || "auto") { |
| 71 | document |
| 72 | .querySelectorAll("starlight-theme-select") |
| 73 | .forEach((picker) => { |
| 74 | const select = picker.querySelector("select"); |
| 75 | if (select) select.value = theme; |
| 76 | const tmpl = document.querySelector("#theme-icons"); |
| 77 | const newIcon = |
| 78 | tmpl && tmpl.content.querySelector("." + theme); |
| 79 | if (newIcon) { |
| 80 | const oldIcon = picker.querySelector("svg.label-icon"); |
| 81 | if (oldIcon) oldIcon.outerHTML = newIcon.outerHTML; |
| 82 | } |
| 83 | }); |
| 84 | }, |
| 85 | }; |
| 86 | })(); |
| 87 | </script> |