|
| 1 | +import { writable } from 'svelte/store'; |
| 2 | +import { browser } from '$app/environment'; |
| 3 | + |
| 4 | +export type Theme = 'light' | 'dark'; |
| 5 | +export type AccentColor = 'indigo' | 'blue' | 'emerald' | 'violet' | 'rose'; |
| 6 | +export type FontSize = 'sm' | 'md' | 'lg'; |
| 7 | + |
| 8 | +export interface ThemePreferences { |
| 9 | + theme: Theme; |
| 10 | + accent: AccentColor; |
| 11 | + fontSize: FontSize; |
| 12 | +} |
| 13 | + |
| 14 | +const STORAGE_KEY = 'stellar-escrow-theme'; |
| 15 | + |
| 16 | +const defaults: ThemePreferences = { theme: 'light', accent: 'indigo', fontSize: 'md' }; |
| 17 | + |
| 18 | +function loadPrefs(): ThemePreferences { |
| 19 | + if (!browser) return defaults; |
| 20 | + try { |
| 21 | + return { ...defaults, ...JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}') }; |
| 22 | + } catch { |
| 23 | + return defaults; |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +function createThemeStore() { |
| 28 | + const { subscribe, set, update } = writable<ThemePreferences>(loadPrefs()); |
| 29 | + |
| 30 | + function persist(prefs: ThemePreferences) { |
| 31 | + if (browser) localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs)); |
| 32 | + applyToDOM(prefs); |
| 33 | + } |
| 34 | + |
| 35 | + return { |
| 36 | + subscribe, |
| 37 | + setTheme: (theme: Theme) => update(p => { const n = { ...p, theme }; persist(n); return n; }), |
| 38 | + setAccent: (accent: AccentColor) => update(p => { const n = { ...p, accent }; persist(n); return n; }), |
| 39 | + setFontSize: (fontSize: FontSize) => update(p => { const n = { ...p, fontSize }; persist(n); return n; }), |
| 40 | + toggle: () => update(p => { const n = { ...p, theme: p.theme === 'light' ? 'dark' : 'light' }; persist(n); return n; }), |
| 41 | + init: () => { const prefs = loadPrefs(); set(prefs); persist(prefs); } |
| 42 | + }; |
| 43 | +} |
| 44 | + |
| 45 | +export const accentColors: Record<AccentColor, { bg: string; text: string; border: string }> = { |
| 46 | + indigo: { bg: '#6366f1', text: '#6366f1', border: '#6366f1' }, |
| 47 | + blue: { bg: '#3b82f6', text: '#3b82f6', border: '#3b82f6' }, |
| 48 | + emerald:{ bg: '#10b981', text: '#10b981', border: '#10b981' }, |
| 49 | + violet: { bg: '#8b5cf6', text: '#8b5cf6', border: '#8b5cf6' }, |
| 50 | + rose: { bg: '#f43f5e', text: '#f43f5e', border: '#f43f5e' }, |
| 51 | +}; |
| 52 | + |
| 53 | +export const fontSizeMap: Record<FontSize, string> = { sm: '14px', md: '16px', lg: '18px' }; |
| 54 | + |
| 55 | +export function applyToDOM(prefs: ThemePreferences) { |
| 56 | + if (!browser) return; |
| 57 | + const root = document.documentElement; |
| 58 | + root.classList.toggle('dark', prefs.theme === 'dark'); |
| 59 | + const color = accentColors[prefs.accent].bg; |
| 60 | + root.style.setProperty('--accent', color); |
| 61 | + root.style.setProperty('--font-size-base', fontSizeMap[prefs.fontSize]); |
| 62 | +} |
| 63 | + |
| 64 | +export const themeStore = createThemeStore(); |
0 commit comments