feat: custom accent color picker in theme preferences

- Added 'custom' accent option with color picker + hex input
- Pipette icon on the custom swatch (switches to solid when active)
- Color picker appears inline when custom is selected
- Generates hover/light variants automatically from hex (darken/lighten)
- Dark mode accent-light uses rgba for translucency
- Persisted in localStorage, applied before React hydration (no flash)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 01:42:38 +01:00
parent 79651bc41d
commit 2eab705a8a
3 changed files with 132 additions and 15 deletions
+8 -7
View File
@@ -14,8 +14,8 @@ import { useThemeStore, applyTheme, resolveTheme, type ThemeMode, type AccentKey
try {
const raw = localStorage.getItem('schaeffler-theme')
if (raw) {
const { state } = JSON.parse(raw) as { state: { mode: ThemeMode; accent: AccentKey } }
applyTheme(state.mode ?? 'light', state.accent ?? 'green')
const { state } = JSON.parse(raw) as { state: { mode: ThemeMode; accent: AccentKey; customHex?: string } }
applyTheme(state.mode ?? 'light', state.accent ?? 'green', state.customHex)
}
} catch {
// ignore — default theme already applied by CSS
@@ -35,21 +35,22 @@ const queryClient = new QueryClient({
function ThemeProvider({ children }: { children: React.ReactNode }) {
const mode = useThemeStore((s) => s.mode)
const accent = useThemeStore((s) => s.accent)
const customHex = useThemeStore((s) => s.customHex)
const resolvedTheme = resolveTheme(mode)
// Apply whenever mode or accent changes
// Apply whenever mode, accent, or customHex changes
useEffect(() => {
applyTheme(mode, accent)
}, [mode, accent])
applyTheme(mode, accent, customHex)
}, [mode, accent, customHex])
// Listen to system preference changes when mode='system'
useEffect(() => {
if (mode !== 'system') return
const mq = window.matchMedia('(prefers-color-scheme: dark)')
const handler = () => applyTheme('system', accent)
const handler = () => applyTheme('system', accent, customHex)
mq.addEventListener('change', handler)
return () => mq.removeEventListener('change', handler)
}, [mode, accent])
}, [mode, accent, customHex])
return (
<>