From 972ac65a2b485dd109fc6d9b73d4a191d0de22dd Mon Sep 17 00:00:00 2001 From: Munkherdene Date: Sat, 22 Aug 2026 23:19:31 +0800 Subject: [PATCH] feat(web): dark mode (system/light/dark) via Seed color-mode + profile toggle --- src/app/layout.tsx | 24 +++++++- src/ds/ThemeToggle.test.tsx | 49 ++++++++++++++++ src/ds/ThemeToggle.tsx | 87 ++++++++++++++++++++++++++++ src/ds/index.ts | 3 + src/features/profile/ProfileView.tsx | 15 ++++- src/features/profile/strings.ts | 4 ++ 6 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 src/ds/ThemeToggle.test.tsx create mode 100644 src/ds/ThemeToggle.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5a71206..d5a47fa 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,9 +2,31 @@ import "./globals.css"; export const metadata = { title: "Mercury" }; +// Runs before paint (in , ahead of hydration) to set the Seed +// color-mode attribute from the persisted preference, avoiding a +// flash-of-incorrect-theme: the server always renders "system" (no +// localStorage access during SSR), so without this the client would briefly +// paint light/system before this script could run post-hydration. +const THEME_INIT_SCRIPT = ` +(function () { + try { + var v = localStorage.getItem("mercury.theme"); + var mode = v === "light" ? "light-only" : v === "dark" ? "dark-only" : "system"; + document.documentElement.setAttribute("data-seed-color-mode", mode); + } catch (e) {} +})(); +`; + export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + // suppressHydrationWarning: the inline script below intentionally + // rewrites data-seed-color-mode before hydration (to the persisted + // preference), so it will legitimately differ from this "system" + // server-rendered value — React should not warn about or "fix" that. + + +