Add dark mode support with persisted OS-aware theme selection

This commit is contained in:
2026-04-21 19:30:29 +02:00
parent 4f91e8fdd3
commit fef8f60ee2
11 changed files with 293 additions and 51 deletions

View File

@@ -28,11 +28,14 @@ import {
ListRegular,
SignOutRegular,
NavigationRegular,
WeatherMoonRegular,
WeatherSunnyRegular,
} from "@fluentui/react-icons";
import { NavLink, Outlet, useNavigate } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";
import { useServerStatus } from "../hooks/useServerStatus";
import { useBlocklistStatus } from "../hooks/useBlocklist";
import { useThemeMode } from "../providers/ThemeProvider";
// ---------------------------------------------------------------------------
// Styles
@@ -146,11 +149,20 @@ const useStyles = makeStyles({
padding: tokens.spacingVerticalS,
flexShrink: 0,
},
footerActions: {
display: "flex",
flexDirection: "column",
gap: tokens.spacingVerticalXS,
},
themeButton: {
width: "100%",
justifyContent: "flex-start",
},
logoutButton: {
width: "100%",
justifyContent: "flex-start",
},
logoutButtonCollapsed: {
sidebarButtonCollapsed: {
justifyContent: "center",
},
versionText: {
@@ -220,6 +232,7 @@ const NAV_ITEMS: NavItem[] = [
export function MainLayout(): React.JSX.Element {
const styles = useStyles();
const { logout } = useAuth();
const { colorMode, toggleColorMode } = useThemeMode();
const navigate = useNavigate();
const readSavedCollapsed = (): boolean => {
@@ -346,31 +359,51 @@ export function MainLayout(): React.JSX.Element {
})}
</ul>
{/* Footer — Logout */}
{/* Footer — Theme toggle and logout */}
<div className={styles.sidebarFooter}>
{!collapsed && (
<Text className={styles.versionText}>
BanGUI
</Text>
)}
<Tooltip
content={collapsed ? "Sign out" : ""}
relationship="label"
positioning="after"
>
<Button
appearance="subtle"
icon={<SignOutRegular />}
onClick={() => void handleLogout()}
aria-label="Sign out"
className={mergeClasses(
styles.logoutButton,
collapsed && styles.logoutButtonCollapsed,
)}
<div className={styles.footerActions}>
<Tooltip
content={collapsed ? (colorMode === "dark" ? "Switch to light mode" : "Switch to dark mode") : ""}
relationship="label"
positioning="after"
>
{!collapsed && "Sign out"}
</Button>
</Tooltip>
<Button
appearance="subtle"
icon={colorMode === "dark" ? <WeatherSunnyRegular /> : <WeatherMoonRegular />}
onClick={toggleColorMode}
aria-label={colorMode === "dark" ? "Switch to light mode" : "Switch to dark mode"}
className={mergeClasses(
styles.themeButton,
collapsed && styles.sidebarButtonCollapsed,
)}
>
{!collapsed && (colorMode === "dark" ? "Light mode" : "Dark mode")}
</Button>
</Tooltip>
<Tooltip
content={collapsed ? "Sign out" : ""}
relationship="label"
positioning="after"
>
<Button
appearance="subtle"
icon={<SignOutRegular />}
onClick={() => void handleLogout()}
aria-label="Sign out"
className={mergeClasses(
styles.logoutButton,
collapsed && styles.sidebarButtonCollapsed,
)}
>
{!collapsed && "Sign out"}
</Button>
</Tooltip>
</div>
</div>
</nav>