feat: Add dark/light theme toggle with localStorage persistence#7
feat: Add dark/light theme toggle with localStorage persistence#7NovaCode37 merged 2 commits intoNovaCode37:mainfrom
Conversation
Implement theme switcher functionality: - Add useTheme hook with localStorage persistence - Add light theme CSS variables alongside dark theme - Add theme toggle button in Topbar with Sun/Moon icons - Respects system color scheme preference as fallback - Smooth transitions between themes Files modified: - frontend/src/app/globals.css: Added theme variables - frontend/src/components/Topbar.tsx: Added toggle button - frontend/src/lib/useTheme.ts: New theme hook (created) The theme preference is stored in localStorage and restored on page load. Falls back to system preference if no saved preference exists. Closes NovaCode37#6
|
Hey, thanks for the PR! Great work on the overall structure - localStorage + system preference fallback is the right approach. However, there are a few issues that need to be fixed before I can merge this: 1. FOUC The theme is applied in useEffect, which runs after React hydration. Users who selected "light" will see a dark→light flash on every page load. Fix: add a blocking inline script in layout.tsx inside using dangerouslySetInnerHTML that reads localStorage('theme') or matchMedia('prefers-color-scheme: light') and sets document.documentElement.setAttribute('data-theme', t) before React renders. This ensures data-theme is set before any CSS is evaluated. 2. Duplicate import in Topbar.tsx - will break the build The diff shows two import lines from lucide-react. Remove the old one without Sun/Moon and keep only the new one that includes Sun and Moon. 3. Redundant hardcoded colors in globals.css The body rule still has hardcoded background: #0d1117 and color: #e6edf3 before the CSS variable versions. Remove both hardcoded lines, keep only background: var(--background) and color: var(--text-1). 4. No CSS transition between themes The PR description mentions "Smooth transitions between themes" but there's no transition in the CSS. Add transition: background-color 0.2s ease, color 0.2s ease to the body rule. 5. Mounted is unused The useTheme hook returns mounted but it's never used in Topbar.tsx. Either use it to conditionally render the toggle button (prevents icon mismatch before hydration) or remove it from the hook. Once these are addressed, I'll be happy to merge. Let me know if you have any questions! |
1. FOUC: Add blocking inline script in layout.tsx that reads localStorage/matchMedia and sets data-theme before React hydrates 2. CSS transition: Add transition: background-color 0.2s ease, color 0.2s ease to body rule in globals.css 3. mounted: Use mounted flag in Topbar to guard icon render and prevent Sun/Moon mismatch before hydration (falls back to Sun)
Summary
Implements a complete theme switcher with persistent storage, allowing users to toggle between dark and light themes. The preference is saved to localStorage and restored on page load.
Changes
Files Modified
frontend/src/app/globals.css
--background,--surface-1,--surface-2,--border-1,--text-1, etc.frontend/src/components/Topbar.tsx
useThemehook and Sun/Moon iconsfrontend/src/lib/useTheme.ts (New)
data-themeattribute on document rootFeatures
✅ Toggle between dark and light themes
✅ Persistent theme preference using localStorage
✅ Respects system color scheme preference
✅ Smooth theme transitions
✅ Accessible UI with proper labels
✅ Sun/Moon icons for visual feedback
Testing
Closes #6