Skip to content

Commit ed1bfe7

Browse files
ananaBMasterclaude
andcommitted
fix: resolve dark mode flash (FOUC) on options and translation-hub pages
Load theme mode before React renders and apply it to <html> immediately, matching the popup's existing pattern. Hydrate baseThemeModeAtom so ThemeProvider has the correct value on first render. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 52a70ff commit ed1bfe7

3 files changed

Lines changed: 35 additions & 6 deletions

File tree

.changeset/fix-dark-mode-fouc.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@read-frog/extension": patch
3+
---
4+
5+
fix: resolve dark mode flash (FOUC) on options and translation-hub pages

src/entrypoints/options/main.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import "@/utils/zod-config"
22
import type { Config } from "@/types/config/config"
3+
import type { ThemeMode } from "@/types/config/theme"
34
import { QueryClientProvider } from "@tanstack/react-query"
45
import { Provider as JotaiProvider } from "jotai"
56
import { useHydrateAtoms } from "jotai/utils"
@@ -14,9 +15,11 @@ import { RecoveryBoundary } from "@/components/recovery/recovery-boundary"
1415
import { SidebarProvider } from "@/components/ui/base-ui/sidebar"
1516
import { TooltipProvider } from "@/components/ui/base-ui/tooltip"
1617
import { configAtom } from "@/utils/atoms/config"
18+
import { baseThemeModeAtom } from "@/utils/atoms/theme"
1719
import { getLocalConfig } from "@/utils/config/storage"
1820
import { DEFAULT_CONFIG } from "@/utils/constants/config"
1921
import { queryClient } from "@/utils/tanstack-query"
22+
import { applyTheme, getLocalThemeMode, isDarkMode } from "@/utils/theme"
2023
import App from "./app"
2124
import { AppSidebar } from "./app-sidebar"
2225
import { SettingsSearch } from "./command-palette/settings-search"
@@ -27,7 +30,10 @@ function HydrateAtoms({
2730
initialValues,
2831
children,
2932
}: {
30-
initialValues: [[typeof configAtom, Config]]
33+
initialValues: [
34+
[typeof configAtom, Config],
35+
[typeof baseThemeModeAtom, ThemeMode],
36+
]
3137
children: React.ReactNode
3238
}) {
3339
useHydrateAtoms(initialValues)
@@ -38,12 +44,18 @@ async function initApp() {
3844
const root = document.getElementById("root")!
3945
root.className = "antialiased bg-background"
4046

41-
const config = (await getLocalConfig()) ?? DEFAULT_CONFIG
47+
const [configValue, themeMode] = await Promise.all([
48+
getLocalConfig(),
49+
getLocalThemeMode(),
50+
])
51+
const config = configValue ?? DEFAULT_CONFIG
52+
53+
applyTheme(document.documentElement, isDarkMode(themeMode) ? "dark" : "light")
4254

4355
ReactDOM.createRoot(root).render(
4456
<React.StrictMode>
4557
<JotaiProvider>
46-
<HydrateAtoms initialValues={[[configAtom, config]]}>
58+
<HydrateAtoms initialValues={[[configAtom, config], [baseThemeModeAtom, themeMode]]}>
4759
<QueryClientProvider client={queryClient}>
4860
<HashRouter>
4961
<SidebarProvider>

src/entrypoints/translation-hub/main.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { Config } from "@/types/config/config"
2+
import type { ThemeMode } from "@/types/config/theme"
23
import { QueryClientProvider } from "@tanstack/react-query"
34
import { Provider as JotaiProvider } from "jotai"
45
import { useHydrateAtoms } from "jotai/utils"
@@ -9,14 +10,19 @@ import { HelpButton } from "@/components/help-button"
910
import { ThemeProvider } from "@/components/providers/theme-provider"
1011
import { TooltipProvider } from "@/components/ui/base-ui/tooltip"
1112
import { configAtom } from "@/utils/atoms/config"
13+
import { baseThemeModeAtom } from "@/utils/atoms/theme"
1214
import { getLocalConfig } from "@/utils/config/storage"
1315
import { DEFAULT_CONFIG } from "@/utils/constants/config"
1416
import { queryClient } from "@/utils/tanstack-query"
17+
import { applyTheme, getLocalThemeMode, isDarkMode } from "@/utils/theme"
1518
import App from "./app"
1619
import "@/assets/styles/theme.css"
1720

1821
interface HydrateAtomsProps {
19-
initialValues: [[typeof configAtom, Config]]
22+
initialValues: [
23+
[typeof configAtom, Config],
24+
[typeof baseThemeModeAtom, ThemeMode],
25+
]
2026
children: React.ReactNode
2127
}
2228

@@ -29,13 +35,19 @@ async function initApp() {
2935
const root = document.getElementById("root")!
3036
root.className = "text-base antialiased min-h-screen bg-background"
3137

32-
const config = (await getLocalConfig()) ?? DEFAULT_CONFIG
38+
const [configValue, themeMode] = await Promise.all([
39+
getLocalConfig(),
40+
getLocalThemeMode(),
41+
])
42+
const config = configValue ?? DEFAULT_CONFIG
43+
44+
applyTheme(document.documentElement, isDarkMode(themeMode) ? "dark" : "light")
3345

3446
ReactDOM.createRoot(root).render(
3547
<React.StrictMode>
3648
<QueryClientProvider client={queryClient}>
3749
<JotaiProvider>
38-
<HydrateAtoms initialValues={[[configAtom, config]]}>
50+
<HydrateAtoms initialValues={[[configAtom, config], [baseThemeModeAtom, themeMode]]}>
3951
<ThemeProvider>
4052
<TooltipProvider>
4153
<App />

0 commit comments

Comments
 (0)