import type { Metadata, Viewport } from 'next' import { headers } from 'next/headers' import { de } from '@/lib/i18n/de' import { THEME_KEY } from '@/components/ThemeToggle' import './globals.css' export const metadata: Metadata = { title: { default: de.app.name, template: `%s · ${de.app.shortName}` }, description: de.app.description, robots: { index: false, follow: false }, } export const viewport: Viewport = { themeColor: '#15181A', width: 'device-width', initialScale: 1, } /** * Setzt das gewählte Thema, **bevor** das erste Mal gemalt wird. * * Ohne das blitzt beim Laden kurz das falsche Thema auf. Es ist die eine * Stelle mit `dangerouslySetInnerHTML`, und der Inhalt ist eine Konstante aus * diesem Modul — kein Nutzertext. Das Verbot aus docs/07 gilt Nutzerinhalten. */ const THEME_SCRIPT = `try{var t=localStorage.getItem(${JSON.stringify(THEME_KEY)});` + `if(t==='dark'||t==='light')document.documentElement.setAttribute('data-theme',t)}catch(e){}` export default async function RootLayout({ children }: { children: React.ReactNode }) { // Die strikte Inhaltsrichtlinie erlaubt nur Skripte mit passendem Nonce, // siehe middleware.ts. const nonce = (await headers()).get('x-nonce') ?? undefined return ( {/* `suppressHydrationWarning` wegen des Nonce, nicht wegen des Inhalts. Der Browser **leert das Nonce-Attribut**, sobald er das Element geparst hat — so schreibt es die CSP-Spezifikation vor, damit kein Skript den Wert über `getAttribute` auslesen und sich selbst nachladen kann. React vergleicht beim Hydrieren aber genau dieses Attribut, liest clientseitig den leeren Wert und meldet einen Unterschied, den es nicht gibt: unser Markup ist auf beiden Seiten dasselbe. Es bleibt bei einem einzigen Element mit einer Konstante als Inhalt, deshalb ist die Unterdrückung hier eng genug. Der Nonce selbst entfällt nicht: ohne ihn blockiert die Richtlinie das Skript, und ohne das Skript blitzt beim Laden das falsche Thema auf. */}