Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 31, 2026

Theme toggle button was non-functional due to SSR/hydration issues. Browser's prefers-color-scheme was ignored on initial load.

Changes

Fixed theme provider hydration mismatch

  • Initialize theme state to "light" during SSR, then detect saved preference or browser setting in useEffect
  • Track mounted state to prevent React from executing theme logic during SSR
  • Apply theme class to documentElement only after mount

Eliminated FOUC with inline script

  • Added blocking script in <head> that runs before React hydration
  • Checks localStorage then prefers-color-scheme media query
  • Applies .dark class to <html> element immediately

Simplified CSS dark mode implementation

  • Removed media query complexity in favor of .dark class selector
  • CSS variables switch based on presence of .dark class on root element

Before/After

Light Mode:
Light Mode

Dark Mode:
Dark Mode

Original prompt

This section details on the original issue you should resolve

<issue_title>Fix white/dark mode</issue_title>
<issue_description>Currently, the button to switch to white mode does not work.
Also, please make sure, that the automatic dark mode settings of the user's browser are used as the standard setting.</issue_description>

Comments on the Issue (you are @copilot in this section)


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

Co-authored-by: AFeuerpfeil <36232041+AFeuerpfeil@users.noreply.github.com>
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Jan 31, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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>
Copilot AI changed the title [WIP] Fix white mode button and dark mode settings Fix dark mode toggle and respect browser preference Jan 31, 2026
Copilot AI requested a review from AFeuerpfeil January 31, 2026 21:00
@AFeuerpfeil AFeuerpfeil marked this pull request as ready for review January 31, 2026 21:02
Copilot AI review requested due to automatic review settings January 31, 2026 21:02
Copy link

Copilot AI left a 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 .dark class before React hydration (reducing FOUC).
  • Simplifies global dark mode styling to switch CSS variables based on the .dark class.

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.

Comment on lines +25 to +33
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');
}
Copy link

Copilot AI Jan 31, 2026

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
--background: #0a0a0a;
--foreground: #ededed;
}
.dark {
Copy link

Copilot AI Jan 31, 2026

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.

Suggested change
.dark {
:root.dark {

Copilot uses AI. Check for mistakes.
Comment on lines +8 to 11
.dark {
--background: #0a0a0a;
--foreground: #ededed;
}
Copy link

Copilot AI Jan 31, 2026

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).

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +25
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";
Copy link

Copilot AI Jan 31, 2026

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).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix white/dark mode

2 participants