fix(admin): apply data-mode attribute for system theme preference (#97)
When theme is "system", ThemeProvider removed the data-mode attribute from <html>, expecting color-scheme: light dark to handle dark mode. However, Tailwind dark: utilities are mapped to [data-mode="dark"] via @custom-variant and do not respond to color-scheme, so dark mode never activated for system preference users. Decouple DOM synchronization from the theme preference by syncing data-mode with resolvedTheme instead. This ensures data-mode is always set to "light" or "dark" regardless of whether the user chose explicitly or follows system preference. Fixes #96 Co-authored-by: Matt Kane <mkane@cloudflare.com>
This commit is contained in:
@@ -44,29 +44,16 @@ export function ThemeProvider({ children, defaultTheme = "system" }: ThemeProvid
|
||||
return theme;
|
||||
});
|
||||
|
||||
// Update DOM and resolved theme when theme changes.
|
||||
// Uses data-mode (not data-theme) for dark mode — kumo's convention.
|
||||
// data-theme is reserved for visual identity overrides.
|
||||
// Update DOM and resolved theme when theme changes.
|
||||
// Uses data-mode (not data-theme) for dark mode — kumo's convention.
|
||||
// data-theme is reserved for visual identity overrides (e.g. "classic").
|
||||
// Resolve the effective theme whenever the user preference changes
|
||||
React.useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
|
||||
// Apply classic visual identity at the root level
|
||||
// so token overrides cascade to all kumo components including portals
|
||||
root.setAttribute("data-theme", "classic");
|
||||
|
||||
if (theme === "system") {
|
||||
root.removeAttribute("data-mode");
|
||||
setResolvedTheme(getSystemTheme());
|
||||
} else {
|
||||
root.setAttribute("data-mode", theme);
|
||||
setResolvedTheme(theme);
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
// Listen for system theme changes when in system mode
|
||||
// Listen for OS preference changes when in system mode
|
||||
React.useEffect(() => {
|
||||
if (theme !== "system") return;
|
||||
|
||||
@@ -79,6 +66,17 @@ export function ThemeProvider({ children, defaultTheme = "system" }: ThemeProvid
|
||||
return () => mediaQuery.removeEventListener("change", handler);
|
||||
}, [theme]);
|
||||
|
||||
// Sync DOM attributes with the resolved theme.
|
||||
// data-mode drives Tailwind dark: utilities via @custom-variant.
|
||||
// data-theme is reserved for visual identity overrides (e.g. "classic").
|
||||
// Always set data-mode explicitly — relying on its absence + color-scheme
|
||||
// does not activate Tailwind dark: utilities which require [data-mode="dark"].
|
||||
React.useEffect(() => {
|
||||
const root = document.documentElement;
|
||||
root.setAttribute("data-theme", "classic");
|
||||
root.setAttribute("data-mode", resolvedTheme);
|
||||
}, [resolvedTheme]);
|
||||
|
||||
const setTheme = React.useCallback((newTheme: Theme) => {
|
||||
setThemeState(newTheme);
|
||||
localStorage.setItem(STORAGE_KEY, newTheme);
|
||||
|
||||
@@ -19,13 +19,10 @@
|
||||
@custom-variant dark (&:where([data-mode="dark"], [data-mode="dark"] *));
|
||||
|
||||
/*
|
||||
* When no data-mode is set (system preference mode), follow the OS preference.
|
||||
* Kumo's base layer sets color-scheme: light on :root — override to light dark
|
||||
* so the browser respects prefers-color-scheme until the user explicitly picks.
|
||||
* ThemeProvider always sets data-mode to the resolved preference ("light" or "dark"),
|
||||
* so Tailwind dark: utilities (mapped to [data-mode="dark"]) activate correctly
|
||||
* for both explicit and system-preference themes.
|
||||
*/
|
||||
:root:not([data-mode]) {
|
||||
color-scheme: light dark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Classic theme token overrides
|
||||
|
||||
Reference in New Issue
Block a user