From 2296610fe3a10c8d47523a2245666f637702a56b Mon Sep 17 00:00:00 2001 From: Robb Fournier Date: Thu, 21 May 2026 14:31:06 -0400 Subject: [PATCH] Add dark mode toggle with CSS variables and localStorage persistence - useDarkMode.js: singleton composable that toggles `html.dark` class and persists choice in localStorage; no flash on reload because class is applied at module-load time before first render - App.vue: CSS custom properties on :root (light) and html.dark (dark) cover page bg, card bg, borders, text, and table colors; useDarkMode() initialized in setup to guarantee early class application - AppSidebar.vue: moon/sun toggle button in sidebar-bottom; collapses to icon-only (32px) when sidebar is collapsed, with tooltip Co-Authored-By: Claude Sonnet 4.6 (1M context) --- client/src/App.vue | 170 +++++++---- client/src/components/AppSidebar.vue | 394 ++++++++++++++++++++++---- client/src/composables/useDarkMode.js | 21 ++ 3 files changed, 461 insertions(+), 124 deletions(-) create mode 100644 client/src/composables/useDarkMode.js diff --git a/client/src/App.vue b/client/src/App.vue index 0221508c..c8414230 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -25,94 +25,99 @@ diff --git a/client/src/composables/useDarkMode.js b/client/src/composables/useDarkMode.js new file mode 100644 index 00000000..51aa70b3 --- /dev/null +++ b/client/src/composables/useDarkMode.js @@ -0,0 +1,21 @@ +import { ref, watch } from "vue"; + +const STORAGE_KEY = "dark-mode"; +const isDark = ref(localStorage.getItem(STORAGE_KEY) === "true"); + +// Apply class to on module load so dark mode is instant (no flash) +if (isDark.value) { + document.documentElement.classList.add("dark"); +} + +watch(isDark, (val) => { + localStorage.setItem(STORAGE_KEY, String(val)); + document.documentElement.classList.toggle("dark", val); +}); + +export function useDarkMode() { + const toggle = () => { + isDark.value = !isDark.value; + }; + return { isDark, toggle }; +}