/** * Theme manager. * * Supports `light` / `dark` / `system`. The resolved theme is written to the * `data-theme` attribute on , which the CSS token layer keys off of. * The `system` mode tracks the OS preference live. The choice is persisted to * localStorage. A tiny inline script in each page sets the initial attribute * before paint to avoid a flash of the wrong theme. */ (function () { function register() { Alpine.store('theme', { mode: 'system', // 'light' | 'dark' | 'system' resolved: 'light', init() { let saved = null; try { saved = localStorage.getItem('yd.theme'); } catch (e) {} if (saved === 'light' || saved === 'dark' || saved === 'system') { this.mode = saved; } this.apply(); const mq = window.matchMedia('(prefers-color-scheme: dark)'); const onChange = () => { if (this.mode === 'system') this.apply(); }; if (mq.addEventListener) mq.addEventListener('change', onChange); else if (mq.addListener) mq.addListener(onChange); }, set(mode) { this.mode = mode; try { localStorage.setItem('yd.theme', mode); } catch (e) {} this.apply(); }, cycle() { const order = ['light', 'dark', 'system']; const next = order[(order.indexOf(this.mode) + 1) % order.length]; this.set(next); }, icon() { if (this.mode === 'system') return 'monitor'; return this.mode === 'dark' ? 'moon' : 'sun'; }, apply() { const mq = window.matchMedia('(prefers-color-scheme: dark)'); const resolved = this.mode === 'system' ? (mq.matches ? 'dark' : 'light') : this.mode; this.resolved = resolved; document.documentElement.setAttribute('data-theme', resolved); }, }); } if (window.Alpine) register(); else document.addEventListener('alpine:init', register); })();