feat(appearance): wire AppearanceProvider and FOUC script into root layout

在 <body> 顶部注入同步执行的 inline 脚本,在 hydration 前为 <html> 写入
data-theme 属性和 font-size 样式;在 ThemeProvider 内嵌套 AppearanceProvider
管理 React 侧 state。两条通道并行运作,互不干扰。

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-11 15:50:25 +08:00
parent ecff8832c0
commit a5cd1ce2a8

View File

@@ -8,6 +8,8 @@ import { getMessagesForLocale } from "@/i18n/messages"
import { resolveRequestLocale } from "@/i18n/resolve-request-locale"
import { ThemeProvider } from "@/components/theme-provider"
import { toIntlLocale } from "@/lib/i18n"
import { APPEARANCE_INIT_SCRIPT } from "@/lib/appearance-script"
import { AppearanceProvider } from "@/components/appearance-provider"
const jetbrainsMono = JetBrains_Mono({
subsets: ["latin"],
@@ -48,6 +50,8 @@ export default async function RootLayout({
suppressHydrationWarning
>
<body>
{/* Apply appearance preferences (theme color + zoom) before first paint to prevent FOUC */}
<script dangerouslySetInnerHTML={{ __html: APPEARANCE_INIT_SCRIPT }} />
{/* Suppress benign ResizeObserver loop warnings (W3C spec §3.3) */}
<script>{`window.addEventListener("error",function(e){if(e.message&&e.message.indexOf("ResizeObserver")!==-1){e.stopImmediatePropagation();e.preventDefault()}});window.onerror=function(m){if(typeof m==="string"&&m.indexOf("ResizeObserver")!==-1)return true}`}</script>
<NextIntlClientProvider
@@ -64,7 +68,7 @@ export default async function RootLayout({
enableSystem
disableTransitionOnChange
>
{children}
<AppearanceProvider>{children}</AppearanceProvider>
</ThemeProvider>
</AppI18nProvider>
</NextIntlClientProvider>