feat: add theme system, fonts, graph styling, rename Overview→Tasks
- Terracotta (light) and Carbon (dark) themes with toggle - Inknut Antiqua headings, DM Sans body - Dot grid background on EntityGraph and GraphBackground - Theme-adaptive graph colors on EntityGraph - Art Nouveau chat styling (borders, underlines, blockquote quotes) - Bullet point styles in chat prose - Task goal in header, rename Overview→Tasks, New Task labels - Logo uses var(--primary) for theme awareness
This commit is contained in:
46
web/src/lib/stores/theme.svelte.ts
Normal file
46
web/src/lib/stores/theme.svelte.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
export type Theme = 'light' | 'dark'
|
||||
|
||||
const STORAGE_KEY = 'oikos-theme'
|
||||
|
||||
function applyClass(theme: Theme): void {
|
||||
const root = document.documentElement
|
||||
if (theme === 'dark') {
|
||||
root.classList.add('dark')
|
||||
} else {
|
||||
root.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
function storedTheme(): Theme {
|
||||
if (typeof localStorage === 'undefined') return 'dark'
|
||||
const v = localStorage.getItem(STORAGE_KEY)
|
||||
if (v === 'light' || v === 'dark') return v
|
||||
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
|
||||
}
|
||||
|
||||
let current: Theme = $state(storedTheme())
|
||||
|
||||
applyClass(current)
|
||||
|
||||
export function setTheme(t: Theme): void {
|
||||
current = t
|
||||
applyClass(t)
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
localStorage.setItem(STORAGE_KEY, t)
|
||||
}
|
||||
}
|
||||
|
||||
export function getTheme(): Theme {
|
||||
return current
|
||||
}
|
||||
|
||||
export function toggleTheme(): Theme {
|
||||
const next = current === 'dark' ? 'light' : 'dark'
|
||||
setTheme(next)
|
||||
return next
|
||||
}
|
||||
|
||||
export const THEME_LABELS: Record<Theme, string> = {
|
||||
light: 'Terracotta',
|
||||
dark: 'Carbon'
|
||||
}
|
||||
Reference in New Issue
Block a user