From 9c0427a1ec77bc72e64e352da97d46e2f69afb5b Mon Sep 17 00:00:00 2001 From: Alexander Heldt Date: Thu, 9 Jul 2026 19:41:30 +0000 Subject: [PATCH] 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 , which the CSS treats as an override (attribute selector beats the media query). A tiny 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. --- src/app.js | 24 ++++++++++++++++++++++++ src/index.html | 14 ++++++++++++++ src/style.css | 51 +++++++++++++++++++++++++++++++++++++++++++++++++- src/sw.js | 2 +- 4 files changed, 89 insertions(+), 2 deletions(-) diff --git a/src/app.js b/src/app.js index 50eab2b..d29cdc0 100644 --- a/src/app.js +++ b/src/app.js @@ -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 + // , which the CSS treats as an override. The 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); } diff --git a/src/index.html b/src/index.html index f330b29..0e23c46 100644 --- a/src/index.html +++ b/src/index.html @@ -9,6 +9,16 @@ +