Blockierendes Init-Skript (themeInitScript) setzt Theme-CSS-Variablen vor dem ersten Paint, verhindert das Aufblitzen des falschen Erscheinungsbilds. resolveInitialScheme vereinheitlicht "gespeicherte Wahl vor Systempraeferenz vor Hell" fuer Skript und React-Anfangszustand. In beide Frontend-Layouts (dms-browser, pdf-viewer) eingebunden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
46 lines
2.3 KiB
TypeScript
46 lines
2.3 KiB
TypeScript
// Reine Funktionen ohne "use client" — bewusst NICHT in ThemeProvider.tsx,
|
|
// weil Next.js jeden Export einer "use client"-Datei als Client-Referenz
|
|
// behandelt: ein Server Component (RootLayout) kann eine solche Funktion
|
|
// dann nicht mehr direkt AUFRUFEN, um ihren Rückgabewert (den Skript-Text)
|
|
// serverseitig zu rendern — das schlug beim ersten Anlauf genau so fehl.
|
|
import { cssVariables, type ColorScheme } from "../tokens/tokens";
|
|
|
|
export const THEME_STORAGE_KEY = "nexarch-shl-theme";
|
|
|
|
function readStoredScheme(): ColorScheme | null {
|
|
if (typeof window === "undefined") return null;
|
|
try {
|
|
const stored = window.localStorage.getItem(THEME_STORAGE_KEY);
|
|
return stored === "light" || stored === "dark" ? stored : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function systemPrefersDark(): boolean {
|
|
if (typeof window === "undefined" || !window.matchMedia) return false;
|
|
return window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
}
|
|
|
|
// resolveInitialScheme ist die EINE Stelle, die "gespeicherte Wahl vor
|
|
// Systempräferenz vor Hell" entscheidet (Akzeptanzkriterien 2+3) — sowohl
|
|
// das blockierende Init-Skript (themeInitScript, verhindert das Aufblitzen
|
|
// des falschen Erscheinungsbilds, Pflichtprüfung 2) als auch der
|
|
// React-Anfangszustand in ThemeProvider rufen dieselbe Logik auf, damit
|
|
// beide nie auseinanderlaufen.
|
|
export function resolveInitialScheme(): ColorScheme {
|
|
return readStoredScheme() ?? (systemPrefersDark() ? "dark" : "light");
|
|
}
|
|
|
|
// themeInitScript liefert ein Skript, das VOR dem ersten Render synchron
|
|
// im <head> ausgeführt wird (siehe RootLayout je Frontend,
|
|
// `strategy="beforeInteractive"`) — es setzt dieselben CSS-Variablen wie
|
|
// cssVariables() bereits vor dem ersten Paint, damit kein falsches
|
|
// Erscheinungsbild kurz aufblitzt, bevor React mountet.
|
|
export function themeInitScript(): string {
|
|
const varsByScheme = { light: cssVariables("light"), dark: cssVariables("dark") };
|
|
return `(function(){try{var vars=${JSON.stringify(varsByScheme)};var stored=localStorage.getItem(${JSON.stringify(
|
|
THEME_STORAGE_KEY,
|
|
)});var scheme=(stored==="light"||stored==="dark")?stored:(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");var v=vars[scheme];var root=document.documentElement;for(var k in v){root.style.setProperty(k,v[k]);}root.dataset.shlTheme=scheme;}catch(e){}})();`;
|
|
}
|