-
Notifications
You must be signed in to change notification settings - Fork 0
Fix dark mode toggle and respect browser preference #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Co-authored-by: AFeuerpfeil <36232041+AFeuerpfeil@users.noreply.github.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
manybodylabwebsite | da42e52 | Jan 31 2026, 08:59 PM |
Co-authored-by: AFeuerpfeil <36232041+AFeuerpfeil@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Fixes the theme toggle under SSR/hydration and ensures the initial theme respects saved preference or the browser’s prefers-color-scheme, while reducing flash of incorrect theme on first paint.
Changes:
- Updates the theme provider to defer theme detection/application until after mount to avoid SSR/hydration issues.
- Adds an inline
<head>bootstrap script to apply the.darkclass before React hydration (reducing FOUC). - Simplifies global dark mode styling to switch CSS variables based on the
.darkclass.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/app/theme-provider.tsx | Defers theme initialization to useEffect, adds a mounted guard, and toggles the root .dark class after mount. |
| src/app/layout.tsx | Injects a pre-hydration script to set the initial .dark class and suppresses hydration warnings on <html>. |
| src/app/globals.css | Switches dark theme variables from a media-query approach to a .dark class-based approach. |
Comments suppressed due to low confidence (1)
src/app/theme-provider.tsx:49
window.localStorage.setItem(...)can throw (quota exceeded / storage disabled). To avoid breaking the toggle interaction, catch and ignore/report storage errors while still updating in-memory theme state.
const newTheme = theme === "light" ? "dark" : "light";
setTheme(newTheme);
window.localStorage.setItem("theme", newTheme);
};
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (savedTheme === 'dark') { | ||
| document.documentElement.classList.add('dark'); | ||
| } | ||
| return; | ||
| } | ||
| const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | ||
| if (isDark) { | ||
| document.documentElement.classList.add('dark'); | ||
| } |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline theme bootstrap script returns early for savedTheme === 'light' without explicitly clearing a pre-existing dark class. While the server markup currently doesn’t set it, making the script idempotent via classList.toggle('dark', savedTheme === 'dark') (and only returning after applying) avoids stale state if the class is ever present for any reason.
| if (savedTheme === 'dark') { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| return; | |
| } | |
| const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | |
| if (isDark) { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| document.documentElement.classList.toggle('dark', savedTheme === 'dark'); | |
| return; | |
| } | |
| const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; | |
| document.documentElement.classList.toggle('dark', isDark); |
| --background: #0a0a0a; | ||
| --foreground: #ededed; | ||
| } | ||
| .dark { |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The theme variables are currently scoped to the generic .dark class selector. This will apply to any element with class dark, not just the root element. To make the intent explicit and avoid accidental scoping changes later, consider using html.dark / :root.dark instead.
| .dark { | |
| :root.dark { |
| .dark { | ||
| --background: #0a0a0a; | ||
| --foreground: #ededed; | ||
| } |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By removing the @media (prefers-color-scheme: dark) fallback and relying solely on the .dark class, users will no longer get automatic dark mode if JavaScript is disabled or the inline script fails. If that fallback behavior matters, consider keeping a media-query-based default (e.g., applying dark variables when prefers-color-scheme: dark and no explicit saved theme is present).
| const savedTheme = window.localStorage.getItem("theme"); | ||
| if (savedTheme === "light" || savedTheme === "dark") { | ||
| return savedTheme; | ||
| } | ||
|
|
||
| const savedTheme = window.localStorage.getItem("theme"); | ||
| if (savedTheme === "light" || savedTheme === "dark") { | ||
| return savedTheme; | ||
| } | ||
| const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches; | ||
| return isDark ? "dark" : "light"; |
Copilot
AI
Jan 31, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getInitialTheme() reads window.localStorage and window.matchMedia without any error handling. Both can throw (e.g., storage blocked/disabled) which would surface as an uncaught error in the mount effect. Consider wrapping these reads in a try/catch and falling back to "light" (or to matchMedia only if available).
Theme toggle button was non-functional due to SSR/hydration issues. Browser's
prefers-color-schemewas ignored on initial load.Changes
Fixed theme provider hydration mismatch
"light"during SSR, then detect saved preference or browser setting inuseEffectmountedstate to prevent React from executing theme logic during SSRdocumentElementonly after mountEliminated FOUC with inline script
<head>that runs before React hydrationprefers-color-schememedia query.darkclass to<html>element immediatelySimplified CSS dark mode implementation
.darkclass selector.darkclass on root elementBefore/After
Light Mode:

Dark Mode:

Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.