diff --git a/src/core/theme.ts b/src/core/theme.ts index 7cbe378..638a13c 100644 --- a/src/core/theme.ts +++ b/src/core/theme.ts @@ -1,6 +1,8 @@ -import { store } from "@store/state.svelte"; +import { store, updateSettings } from "@store/state.svelte"; let themeStyleEl: HTMLStyleElement | null = null; +let mediaQuery: MediaQueryList | null = null; +let mediaHandler: (() => void) | null = null; export function applyTheme() { const themeId = store.settings.theme ?? "dark"; @@ -34,3 +36,32 @@ export function applyTheme() { themeStyleEl.textContent = css; document.documentElement.setAttribute("data-theme", "custom"); } + +function applySystemTheme(dark: boolean) { + const themeId = dark + ? (store.settings.systemThemeDark ?? "dark") + : (store.settings.systemThemeLight ?? "light"); + updateSettings({ theme: themeId }); +} + +export function mountSystemThemeSync() { + if (mediaQuery && mediaHandler) { + mediaQuery.removeEventListener("change", mediaHandler); + mediaHandler = null; + } + + if (!store.settings.systemThemeSync) return; + + mediaQuery = window.matchMedia("(prefers-color-scheme: dark)"); + mediaHandler = () => applySystemTheme(mediaQuery!.matches); + mediaQuery.addEventListener("change", mediaHandler); + applySystemTheme(mediaQuery.matches); +} + +export function unmountSystemThemeSync() { + if (mediaQuery && mediaHandler) { + mediaQuery.removeEventListener("change", mediaHandler); + mediaHandler = null; + mediaQuery = null; + } +} \ No newline at end of file diff --git a/src/features/settings/components/Settings.css b/src/features/settings/components/Settings.css index 8de23f1..25507c2 100644 --- a/src/features/settings/components/Settings.css +++ b/src/features/settings/components/Settings.css @@ -226,7 +226,8 @@ flex-shrink: 0; transition: background var(--t-base), border-color var(--t-base); } -.s-toggle.on { background: var(--accent); border-color: var(--accent); } +.s-toggle.on, +.s-toggle-on { background: var(--accent); border-color: var(--accent); } .s-toggle-thumb { position: absolute; @@ -238,12 +239,46 @@ background: var(--text-faint); transition: transform var(--t-base), background var(--t-base); } -.s-toggle.on .s-toggle-thumb { +.s-toggle.on .s-toggle-thumb, +.s-toggle-on .s-toggle-thumb { transform: translateX(15px); background: var(--bg-void); } +/* ── System theme sync pair ───────────────────────────────────────── */ +.s-sync-pair { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1px; + border-top: 1px solid var(--border-dim); + background: var(--border-dim); +} + +.s-sync-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-3); + padding: 8px var(--sp-4); + background: var(--bg-raised); +} + +.s-sync-label { + font-family: var(--font-ui); + font-size: var(--text-xs); + color: var(--text-faint); + letter-spacing: var(--tracking-wide); + flex-shrink: 0; +} + +.s-sync-item .s-select-btn { + font-size: var(--text-xs); + min-width: 0; + padding: 4px 8px; +} + + /* ── Stepper ──────────────────────────────────────────────────────── */ .s-stepper { display: flex; diff --git a/src/features/settings/components/Settings.svelte b/src/features/settings/components/Settings.svelte index 04f656b..0edb693 100644 --- a/src/features/settings/components/Settings.svelte +++ b/src/features/settings/components/Settings.svelte @@ -162,7 +162,7 @@ {#if tab === "general"} {:else if tab === "appearance"} - + {:else if tab === "reader"} {:else if tab === "library"} diff --git a/src/features/settings/sections/AppearanceSettings.svelte b/src/features/settings/sections/AppearanceSettings.svelte index f0b0249..7823108 100644 --- a/src/features/settings/sections/AppearanceSettings.svelte +++ b/src/features/settings/sections/AppearanceSettings.svelte @@ -1,12 +1,18 @@
+
+
+
+ Match system theme + Automatically switch theme when your OS switches between light and dark +
+ +
+ + {#if store.settings.systemThemeSync} +
+
+ Dark theme +
+ + {#if selectOpen === "sync-dark" || closingSelect === "sync-dark"} +
+ {#each allThemeOptions as opt} + + {/each} +
+ {/if} +
+
+
+ Light theme +
+ + {#if selectOpen === "sync-light" || closingSelect === "sync-light"} +
+ {#each allThemeOptions as opt} + + {/each} +
+ {/if} +
+
+
+ {/if} +
+

Theme