Merge pull request #1033

feat(ui): add system theme mode for dark mode controls
This commit is contained in:
Enrico Ros
2026-03-21 16:11:56 -07:00
committed by GitHub
2 changed files with 54 additions and 17 deletions
+53 -16
View File
@@ -1,8 +1,10 @@
import * as React from 'react';
import { Button, IconButton, useColorScheme } from '@mui/joy';
import BrightnessAutoIcon from '@mui/icons-material/BrightnessAuto';
import DarkModeIcon from '@mui/icons-material/DarkMode';
import LightModeIcon from '@mui/icons-material/LightMode';
import { GoodTooltip } from './GoodTooltip';
export const darkModeToggleButtonSx = {
boxShadow: 'sm',
@@ -12,29 +14,64 @@ export const darkModeToggleButtonSx = {
},
} as const;
type ThemeMode = 'light' | 'dark' | 'system';
const _nextThemeMode: Record<ThemeMode, ThemeMode> = {
light: 'dark',
dark: 'system',
system: 'light',
};
const _themeModeLabel: Record<ThemeMode, string> = {
light: 'Light Theme',
dark: 'Dark Theme',
system: 'System Theme',
};
function _themeModeIcon(mode: ThemeMode) {
switch (mode) {
case 'dark':
return <DarkModeIcon />;
case 'system':
return <BrightnessAutoIcon />;
case 'light':
default:
return <LightModeIcon />;
}
}
export function DarkModeToggleButton(props: { hasText?: boolean }) {
// external state
const { mode: colorMode, setMode: setColorMode } = useColorScheme();
const mode: ThemeMode = colorMode === 'light' || colorMode === 'dark' || colorMode === 'system'
? colorMode
: 'system';
const handleToggleDarkMode = (event: React.MouseEvent) => {
event.stopPropagation();
setColorMode(colorMode === 'dark' ? 'light' : 'dark');
setColorMode(_nextThemeMode[mode]);
};
return props.hasText ? (
<Button
variant='soft'
color='neutral'
onClick={handleToggleDarkMode}
sx={darkModeToggleButtonSx}
startDecorator={colorMode !== 'dark' ? <DarkModeIcon color='primary' /> : <LightModeIcon />}
>
{colorMode === 'dark' ? 'Light Mode' : 'Dark Mode'}
</Button>
) : (
<IconButton size='sm' variant='soft' onClick={handleToggleDarkMode} sx={{ ml: 'auto', /*mr: '2px',*/ my: '-0.25rem' /* absorb the menuItem padding */ }}>
{colorMode !== 'dark' ? <DarkModeIcon /> : <LightModeIcon />}
</IconButton>
const title = `Theme: ${_themeModeLabel[mode]}`;
return (
<GoodTooltip title={title}>
{props.hasText ? (
<Button
variant='soft'
color='neutral'
onClick={handleToggleDarkMode}
sx={darkModeToggleButtonSx}
startDecorator={React.cloneElement(_themeModeIcon(mode), { color: 'primary' })}
>
{_themeModeLabel[mode]}
</Button>
) : (
<IconButton size='sm' variant='soft' onClick={handleToggleDarkMode} sx={{ ml: 'auto', /*mr: '2px',*/ my: '-0.25rem' /* absorb the menuItem padding */ }}>
{_themeModeIcon(mode)}
</IconButton>
)}
</GoodTooltip>
);
}
}
+1 -1
View File
@@ -61,7 +61,7 @@ export const ProviderTheming = (props: { emotionCache?: EmotionCache, children:
return (
<CacheProvider value={props.emotionCache || clientSideEmotionCache}>
<CssVarsProvider defaultMode='light' theme={theme}>
<CssVarsProvider defaultMode='system' theme={theme}>
<CssBaseline />
{/* Inject sprites to be referenced by SVG rendering */}
<VendorIconSpriteMemo />