0352c22b4f
Three holistic design directions with CSS custom properties, a theme store persisted to localStorage, and a live switcher in both the header (cycle button) and settings page (card selector). Also fixes checkbox checkmark alignment and adds back navigation from settings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
58 lines
1.3 KiB
JavaScript
58 lines
1.3 KiB
JavaScript
import { writable } from 'svelte/store';
|
|
import { browser } from '$app/environment';
|
|
|
|
/** @typedef {'obsidian' | 'paper' | 'midnight'} ThemeName */
|
|
|
|
const STORAGE_KEY = 'opal-theme';
|
|
|
|
/** @type {ThemeName} */
|
|
const DEFAULT_THEME = 'obsidian';
|
|
|
|
/** @type {ThemeName[]} */
|
|
export const THEMES = ['obsidian', 'paper', 'midnight'];
|
|
|
|
/**
|
|
* Read stored theme, falling back to default
|
|
* @returns {ThemeName}
|
|
*/
|
|
function getInitial() {
|
|
if (!browser) return DEFAULT_THEME;
|
|
const stored = localStorage.getItem(STORAGE_KEY);
|
|
if (stored && THEMES.includes(/** @type {ThemeName} */ (stored))) {
|
|
return /** @type {ThemeName} */ (stored);
|
|
}
|
|
return DEFAULT_THEME;
|
|
}
|
|
|
|
function createThemeStore() {
|
|
const { subscribe, set, update } = writable(getInitial());
|
|
|
|
/** Apply theme to the document */
|
|
function apply(/** @type {ThemeName} */ theme) {
|
|
if (browser) {
|
|
document.documentElement.dataset.theme = theme;
|
|
localStorage.setItem(STORAGE_KEY, theme);
|
|
}
|
|
}
|
|
|
|
// Apply on every change
|
|
subscribe(apply);
|
|
|
|
return {
|
|
subscribe,
|
|
/** @param {ThemeName} theme */
|
|
set(theme) {
|
|
set(theme);
|
|
},
|
|
/** Cycle to the next theme */
|
|
cycle() {
|
|
update(current => {
|
|
const idx = THEMES.indexOf(current);
|
|
return THEMES[(idx + 1) % THEMES.length];
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
export const themeStore = createThemeStore();
|