// App appearance prefs (theme + a few visual knobs), persisted to localStorage // and applied by toggling attributes / CSS variables on . Kept out of a // component module so it can be imported anywhere (and called once at boot in // main.tsx before React renders, to avoid a dark→light flash). export type ThemeMode = 'dark' | 'light' export interface AppearancePrefs { theme: ThemeMode fontSize: number // base font-size in px radius: number // card border-radius in px animations: boolean } const PREFS_KEY = 'archnest-appearance-prefs' export function defaultAppearance(): AppearancePrefs { return { theme: 'dark', fontSize: 14, radius: 12, animations: true } } export function loadAppearance(): AppearancePrefs { try { const raw = localStorage.getItem(PREFS_KEY) if (raw) return { ...defaultAppearance(), ...JSON.parse(raw) } } catch { /* ignore malformed local storage */ } return defaultAppearance() } export function saveAppearance(prefs: AppearancePrefs) { localStorage.setItem(PREFS_KEY, JSON.stringify(prefs)) } // Apply prefs to the document. Light mode flips data-theme="light" (which the // CSS variable overrides in index.css key off of); dark removes the attribute. // ponytail: only theme is wired to real CSS right now; fontSize/radius/animations // are persisted and exposed as CSS vars for components to opt into later — the // app still mostly hardcodes hex, so a full token migration is a separate task // (tracked in ROADMAP "Appearance section"). export function applyAppearance(prefs: AppearancePrefs) { const root = document.documentElement if (prefs.theme === 'light') root.setAttribute('data-theme', 'light') else root.removeAttribute('data-theme') root.style.setProperty('--app-font-size', `${prefs.fontSize}px`) root.style.setProperty('--card-radius', `${prefs.radius}px`) root.style.setProperty('--app-animations', prefs.animations ? '1' : '0') }