dev_arc_aws/src/lib/theme.ts
Samuel James 08139ff831
Make Appearance light mode work (gray theme) + roadmap GNOME/KDE RDP (#50)
Appearance tab was local-state-only — light mode did nothing. Wire it up:
- index.css: theme color tokens are now CSS variables on :root (dark default)
  with a [data-theme="light"] override using a soft GRAY page background
  (#E4E6EB), not white. The @theme tokens + html/body + select reference the
  vars, so the app shell and any component using bg-page/text-text-primary/etc.
  themes automatically.
- New src/lib/theme.ts: localStorage-backed appearance prefs (theme/fontSize/
  radius/animations) + applyAppearance() toggling data-theme on <html>, mirroring
  the terminalPrefs pattern.
- main.tsx applies saved theme before first render (no flash).
- Settings AppearanceSection persists + applies on change; theme/fontSize/radius/
  animations are live. Dropped the non-functional "Sidebar Expanded by default"
  toggle. (accent color is still cosmetic-only — full token migration of
  hardcoded-hex pages is a separate task, noted in ROADMAP.)

Also adds the Remote Desktop GNOME & KDE support work to ROADMAP as an add-on
(XFCE confirmed working; GNOME needs a FreeRDP-3 guacd image, KDE via xrdp +
startplasma-x11). Full detail in docs/rdp-debug-handoff.md.

Co-authored-by: Samuel James <ssamjame@amazon.com>
Co-authored-by: Kiro <noreply@kiro.dev>
2026-06-22 16:39:50 -04:00

48 lines
1.9 KiB
TypeScript

// App appearance prefs (theme + a few visual knobs), persisted to localStorage
// and applied by toggling attributes / CSS variables on <html>. 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')
}