Files
RecipesCam/docker/frontend/src/theme/ThemeProvider.tsx
T
3dtours 8259bd468d web: move the section jumps to their own strip and grow the Theme menu
The six section links leave the top bar for a thin shelf under it, each a
bordered pill at the page's normal text size, so the bar itself stays a
row of actions and the jumps still read as buttons.

The Theme menu gains the workspace's colour groups — the landing palette
now takes its hue from the accent tokens — an Auto mode that follows the
OS scheme live, and a dropdown in place of the three font chips.
2026-09-18 11:30:12 +07:00

51 lines
2.0 KiB
TypeScript

import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
import { ACCENTS, applyTheme, readAccent, readFonts, readTheme, type AccentId, type FontSetId, type ThemeMode } from './themes';
interface ThemeValue {
mode: ThemeMode;
accent: AccentId;
fonts: FontSetId;
setMode: (mode: ThemeMode) => void;
setAccent: (accent: AccentId) => void;
setFonts: (fonts: FontSetId) => void;
}
const Ctx = createContext<ThemeValue | null>(null);
// The tokens live in CSS; this provider only owns the three attributes on
// <html> and the persisted choice. No component ever reads a colour from JS.
export function ThemeProvider({ children }: { children: ReactNode }) {
const [mode, setModeState] = useState<ThemeMode>(readTheme);
const [accent, setAccentState] = useState<AccentId>(readAccent);
const [fonts, setFontsState] = useState<FontSetId>(readFonts);
useEffect(() => {
applyTheme(mode, accent, fonts);
// Auto has to follow the OS while the page is open, not just at mount.
if (mode !== 'auto') return;
const media = window.matchMedia('(prefers-color-scheme: light)');
const onChange = () => applyTheme(mode, accent, fonts);
media.addEventListener('change', onChange);
return () => media.removeEventListener('change', onChange);
}, [mode, accent, fonts]);
const setMode = useCallback((next: ThemeMode) => setModeState(next), []);
const setAccent = useCallback((next: AccentId) => setAccentState(next), []);
const setFonts = useCallback((next: FontSetId) => setFontsState(next), []);
const value = useMemo(
() => ({ mode, accent, fonts, setMode, setAccent, setFonts }),
[mode, accent, fonts, setMode, setAccent, setFonts]
);
return <Ctx.Provider value={value}>{children}</Ctx.Provider>;
}
export function useTheme(): ThemeValue {
const value = useContext(Ctx);
if (!value) throw new Error('useTheme used outside ThemeProvider');
return value;
}
export { ACCENTS, FONT_SETS } from './themes';
export type { AccentId, FontSetId, ThemeMode } from './themes';