Add light/dark mode toggle in settings

Settings gains a "Dark mode" switch. Theme preference is device-global
(localStorage), independent of accounts. With no explicit choice the app keeps
following the OS via prefers-color-scheme; picking a mode sets data-theme on
<html>, which the CSS treats as an override (attribute selector beats the media
query). A tiny <head> script applies a saved choice before first paint to avoid
a light/dark flash. Toggling previews live, independent of Save/Cancel.

Bumps the service-worker cache. Verified in a headless-browser run: default
follows OS, enabling dark swaps the palette, the choice persists across reload,
and toggling back restores light.
This commit is contained in:
Alexander Heldt
2026-07-09 19:41:30 +00:00
parent 5c016ca49e
commit 9c0427a1ec
4 changed files with 89 additions and 2 deletions
+24
View File
@@ -1485,15 +1485,39 @@
});
// Settings dialog (puppy name + birthday)
// ---------- theme ----------
// Preference is device-global (not per user). No stored choice → follow the
// OS via the prefers-color-scheme media query; a choice sets data-theme on
// <html>, which the CSS treats as an override. The <head> applies any saved
// choice before first paint; this just resolves state and reacts to the toggle.
const THEME_KEY = "puppy-tracker:theme";
const prefersDark = () =>
window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
function effectiveTheme() {
const s = localStorage.getItem(THEME_KEY);
return s === "light" || s === "dark" ? s : (prefersDark() ? "dark" : "light");
}
function setTheme(theme) {
document.documentElement.dataset.theme = theme;
try { localStorage.setItem(THEME_KEY, theme); } catch { /* ignore */ }
}
const settingsDialog = document.getElementById("settings-dialog");
const settingsForm = document.getElementById("settings-form");
const settingsName = document.getElementById("settings-name");
const settingsBirthday = document.getElementById("settings-birthday");
const settingsTheme = document.getElementById("settings-theme");
// Apply live so the toggle previews immediately (independent of Save/Cancel).
settingsTheme.addEventListener("change", () => {
setTheme(settingsTheme.checked ? "dark" : "light");
});
function openSettingsDialog() {
const cfg = loadConfig();
settingsName.value = cfg.name;
settingsBirthday.value = cfg.birthday;
settingsTheme.checked = effectiveTheme() === "dark";
settingsDialog.showModal();
setTimeout(() => settingsName.focus(), 50);
}