71 lines
2.2 KiB
JavaScript
71 lines
2.2 KiB
JavaScript
// This small, blocking head script applies the theme before the first paint.
|
|
(() => {
|
|
const root = document.documentElement;
|
|
const system = window.matchMedia("(prefers-color-scheme: dark)");
|
|
const storageKey = "rea-theme";
|
|
const parseChoice = (value) =>
|
|
value === "light" || value === "dark" ? value : null;
|
|
let choice = null;
|
|
let storage = null;
|
|
try {
|
|
storage = window.localStorage;
|
|
choice = parseChoice(storage.getItem(storageKey));
|
|
} catch {
|
|
// The switch still works when browser storage is unavailable.
|
|
}
|
|
|
|
const applyTheme = () => {
|
|
const theme = choice ?? (system.matches ? "dark" : "light");
|
|
root.dataset.theme = theme;
|
|
document.querySelector('meta[name="theme-color"]').content =
|
|
theme === "dark" ? "#1e1e2e" : "#ffffff";
|
|
window.dispatchEvent(new Event("website-theme-change"));
|
|
};
|
|
|
|
applyTheme();
|
|
system.addEventListener("change", () => {
|
|
if (choice === null) applyTheme();
|
|
});
|
|
window.addEventListener("storage", (event) => {
|
|
if (
|
|
storage !== null &&
|
|
event.storageArea === storage &&
|
|
(event.key === storageKey || event.key === null)
|
|
) {
|
|
try {
|
|
// Another tab may have written a newer choice while this event waited.
|
|
choice = parseChoice(storage.getItem(storageKey));
|
|
} catch {
|
|
return;
|
|
}
|
|
applyTheme();
|
|
}
|
|
});
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
const button = document.querySelector("[data-theme-toggle]");
|
|
if (button === null) return;
|
|
const { t } = await import("./i18n.js");
|
|
const updateLabel = () => {
|
|
const label =
|
|
root.dataset.theme === "dark"
|
|
? t("Switch to light mode")
|
|
: t("Switch to dark mode");
|
|
button.setAttribute("aria-label", label);
|
|
button.title = label;
|
|
};
|
|
window.addEventListener("website-theme-change", updateLabel);
|
|
button.addEventListener("click", () => {
|
|
choice = root.dataset.theme === "dark" ? "light" : "dark";
|
|
try {
|
|
storage?.setItem(storageKey, choice);
|
|
} catch {
|
|
// Retain the choice for this page even if it cannot be saved.
|
|
}
|
|
applyTheme();
|
|
});
|
|
updateLabel();
|
|
button.hidden = false;
|
|
});
|
|
})();
|