fix: add DarkModeSync to propagate tldraw dark mode to document root

tldraw v3.6 sets tl-theme__dark class on .tl-container, not data-color-mode
on document.documentElement. DarkModeSync lives inside TLDrawProvider and
mirrors the active color scheme to [data-color-mode] on <html>, activating
--cc-* token overrides in cc-design-system.css globally.
This commit is contained in:
CC Worker 2026-06-01 00:47:24 +00:00
parent 01a8a59349
commit 489c2208c5

View File

@ -1,5 +1,5 @@
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import { useMemo } from 'react'; import { useMemo, useEffect } from 'react';
import { ThemeProvider, createTheme } from '@mui/material/styles'; import { ThemeProvider, createTheme } from '@mui/material/styles';
import { useTLDraw } from './contexts/TLDrawContext'; import { useTLDraw } from './contexts/TLDrawContext';
import { theme } from './services/themeService'; import { theme } from './services/themeService';
@ -10,26 +10,40 @@ import AppRoutes from './AppRoutes';
import { ErrorBoundary } from './components/ErrorBoundary'; import { ErrorBoundary } from './components/ErrorBoundary';
import React from 'react'; import React from 'react';
const App = React.memo(() => { /**
* Syncs tldraw's color scheme to data-color-mode on <html>.
* Must live inside TLDrawProvider so useTLDraw() reads live preferences.
* This activates the [data-color-mode="dark"] overrides in cc-design-system.css.
* tldraw itself uses tl-theme__dark class on .tl-container we mirror that
* to the document root so --cc-* token overrides apply globally.
*/
const DarkModeSync: React.FC = () => {
const { tldrawPreferences } = useTLDraw(); const { tldrawPreferences } = useTLDraw();
const prefersDarkMode =
typeof window !== 'undefined' &&
window.matchMedia('(prefers-color-scheme: dark)').matches;
useEffect(() => {
const scheme = tldrawPreferences?.colorScheme ?? 'system';
const isDark = scheme === 'dark' || (scheme === 'system' && prefersDarkMode);
document.documentElement.setAttribute('data-color-mode', isDark ? 'dark' : 'light');
}, [tldrawPreferences?.colorScheme, prefersDarkMode]);
return null;
};
const App = React.memo(() => {
const prefersDarkMode = const prefersDarkMode =
typeof window !== 'undefined' && typeof window !== 'undefined' &&
window.matchMedia('(prefers-color-scheme: dark)').matches; window.matchMedia('(prefers-color-scheme: dark)').matches;
const appTheme = useMemo(() => { const appTheme = useMemo(() => {
let mode: 'light' | 'dark'; // App is outside TLDrawProvider so tldrawPreferences is always null here.
// Theme defaults to OS preference; DarkModeSync (inside TLDrawProvider) handles
if (tldrawPreferences?.colorScheme === 'system' || tldrawPreferences == null) { // updating data-color-mode once tldraw preferences are loaded.
mode = prefersDarkMode ? 'dark' : 'light'; const mode = prefersDarkMode ? 'dark' : 'light';
} else { return createTheme({ palette: { mode } });
mode = tldrawPreferences?.colorScheme === 'dark' ? 'dark' : 'light'; }, [prefersDarkMode]);
}
return createTheme({
palette: { mode },
});
}, [tldrawPreferences?.colorScheme, prefersDarkMode]);
return ( return (
<ErrorBoundary> <ErrorBoundary>
@ -37,6 +51,7 @@ const App = React.memo(() => {
<AuthProvider> <AuthProvider>
<UserProvider> <UserProvider>
<TLDrawProvider> <TLDrawProvider>
<DarkModeSync />
<ThemeProvider theme={appTheme}> <ThemeProvider theme={appTheme}>
<AppRoutes /> <AppRoutes />
</ThemeProvider> </ThemeProvider>