dark mode#249
Open
codefather2026 wants to merge 1 commit into
Open
Conversation
|
@codefather2026 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix #243: Replace Manual Theme Mutation with next-themes Provider System
Summary
This PR addresses the issue where the application was managing dark mode through direct documentElement mutation, with no persistence or hydration safety — resulting in theme flicker on navigation, inconsistent state across routes, and a poor developer experience.
Problem Statement
The previous implementation was:
Mutating document.documentElement directly for theme changes
Providing no theme persistence across sessions
Causing hydration mismatches and flash-of-incorrect-theme (FOIT)
Scattering theme logic without a single source of truth
Breaking dark backgrounds on referral pages
Lacking a globally accessible theme toggle
Solution Implemented
✨ New Dark Mode Architecture
next-themes integration at the app root for global coverage
localStorage persistence so user preference survives page reloads
Instant propagation across all routes without manual re-wiring
System preference support as the default fallback
Provider mounted at root so all child routes inherit theme context
No per-route theme wiring needed going forward
ThemeProvider composed within the client provider tree
Correct SSR boundary placement to prevent hydration errors
Floating app-wide control accessible from any route
Provider-backed state replaces direct DOM mutation
Single source of truth for reading and setting the active theme
Refactored to use provider state instead of documentElement manipulation
Accessible markup with proper ARIA attributes
Suppressed hydration mismatch using mounted guard pattern
src/app/referral/page.tsx (line 13) — updated to honor dark background tokens
src/app/referral/leaderboard/page.tsx (line 13) — same dark mode background fix applied
color-scheme CSS property set per theme to ensure native UI elements (scrollbars, inputs) respect the active mode
Smooth transitions between light and dark without layout shift
🎯 Acceptance Criteria Met
✅ Theme changes apply at the app root
ThemeProvider wraps the full component tree via layout.tsx
No route requires individual theme setup
✅ Theme persists in localStorage
next-themes handles read/write automatically
Preference survives hard refreshes and new sessions
✅ Theme updates instantly across routes
Provider context propagates changes reactively
No full-page reload or manual DOM sync needed
✅ Manual documentElement mutation removed
ThemeSwitcher.tsx and GlobalThemeToggle.tsx both use provider state exclusively
No direct DOM theme manipulation anywhere in the codebase
✅ Hydration safety implemented
ThemeSwitcher.tsx uses a mounted guard to prevent SSR/client mismatch
No flash-of-incorrect-theme on initial load
✅ Referral pages respect dark mode
Both referral routes updated with correct dark background classes
Consistent with the rest of the app's theme tokens
📝 Migration Guide
For Existing Components
Replace document.documentElement.classList.toggle('dark') → const { setTheme } = useTheme()
Replace manual localStorage.setItem('theme', ...) → handled automatically by next-themes
Add mounted guard before rendering theme-dependent UI to avoid hydration errors
For New Development
Use useTheme() hook from next-themes to read or set the theme
Wrap any SSR-sensitive theme UI in a mounted check
Use CSS color-scheme for native element theming in new stylesheets
🔮 Future Enhancements
Per-user theme preference synced server-side
Additional theme variants beyond light/dark (e.g. high-contrast)
Animated theme transition effects
📋 Files Changed
Created: src/utils/ThemeProvider.tsx
Modified: src/app/layout.tsx
Modified: src/components/ClientProviders.tsx
Created: src/components/GlobalThemeToggle.tsx
Modified: src/components/ThemeSwitcher.tsx
Modified: src/app/referral/page.tsx
Modified: src/app/referral/leaderboard/page.tsx
Modified: src/app/globals.css
✅ Verification
next-themes provider wired at app root
Theme persists in localStorage across sessions
Instant theme propagation across all routes
All manual documentElement mutation removed
Hydration-safe ThemeSwitcher implemented
GlobalThemeToggle accessible app-wide
Referral pages honor dark mode backgrounds
color-scheme property set in global CSS