Mercurial
diff mrjunejune/src/index.js @ 256:30c2196d03d4
[site] Integrate Zenbu themes and components
Co-authored-by: Copilot <[email protected]>
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Tue, 04 Aug 2026 16:49:01 -0700 |
| parents | bcc76a156aea |
| children | 60a876c4587a |
line wrap: on
line diff
--- a/mrjunejune/src/index.js Tue Aug 04 15:12:18 2026 -0700 +++ b/mrjunejune/src/index.js Tue Aug 04 16:49:01 2026 -0700 @@ -1,50 +1,74 @@ const THEME_STORAGE_KEY = 'theme-preference'; -// Get stored preference or detect system preference -function getThemePreference() -{ - const stored = localStorage.getItem(THEME_STORAGE_KEY); - if (stored) { - return stored; - } - return 'auto'; +const THEMES = ['auto', 'paper', 'ink', 'playful']; +const THEME_LABELS = { + auto: 'Auto', + paper: 'Paper', + ink: 'Ink', + playful: 'Playful', +}; + +function normalizeTheme(value) { + if (value === 'light') return 'paper'; + if (value === 'dark') return 'ink'; + return THEMES.includes(value) ? value : 'auto'; +} + +function getThemePreference() { + return normalizeTheme(localStorage.getItem(THEME_STORAGE_KEY)); +} + +function updateThemeColor() { + const probe = document.createElement('span'); + probe.style.background = 'var(--zen-color-canvas)'; + document.body.append(probe); + const color = getComputedStyle(probe).backgroundColor; + probe.remove(); + document.querySelector('meta[name="theme-color"]') + ?.setAttribute('content', color); } -function applyTheme(preference) -{ - const root = document.documentElement; - - if (preference === 'light') { - root.classList.remove('dark', 'auto'); - root.classList.add('light-mode'); - } else if (preference === 'dark') { - root.classList.remove('light-mode', 'auto'); - root.classList.add('dark'); - } else { - // Auto - remove manual overrides and let CSS media query handle it - root.classList.remove('light-mode', 'dark'); - root.classList.add('auto'); - } +function updateThemeControl(preference) { + const toggle = document.querySelector('#themeToggle'); + const label = document.querySelector('#themeName'); + if (label) label.textContent = THEME_LABELS[preference]; + toggle?.setAttribute( + 'aria-label', + `Change site theme. Current theme: ${THEME_LABELS[preference]}`, + ); } -(function() { - const currentPreference = getThemePreference(); - applyTheme(currentPreference); -})(); - -/* Toggle lights auto and dark */ -const toggle = document.querySelector('#themeToggle'); -toggle.addEventListener('click', function() { - let preference = getThemePreference(); +function applyTheme(value, persist = false) { + const preference = normalizeTheme(value); + const root = document.documentElement; + root.classList.remove('light-mode', 'dark', 'auto'); + if (preference === 'auto') delete root.dataset.zenTheme; + else root.dataset.zenTheme = preference; + if (persist) localStorage.setItem(THEME_STORAGE_KEY, preference); + updateThemeControl(preference); + requestAnimationFrame(updateThemeColor); + document.dispatchEvent(new CustomEvent('zen-theme-change', { + detail: { theme: preference }, + })); + return preference; +} - // Cycle through: auto -> light -> dark -> auto - if (preference === 'auto') { - preference = 'light'; - } else if (preference === 'light') { - preference = 'dark'; - } else { - preference = 'auto'; - } +const currentPreference = applyTheme(getThemePreference()); +if (localStorage.getItem(THEME_STORAGE_KEY) !== currentPreference) { + localStorage.setItem(THEME_STORAGE_KEY, currentPreference); +} - localStorage.setItem(THEME_STORAGE_KEY, preference); - applyTheme(preference); +document.querySelector('#themeToggle')?.addEventListener('click', () => { + const current = getThemePreference(); + const next = THEMES[(THEMES.indexOf(current) + 1) % THEMES.length]; + applyTheme(next, true); }); + +matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + if (getThemePreference() === 'auto') updateThemeColor(); +}); + +window.MrJuneJuneTheme = Object.freeze({ + apply: theme => applyTheme(theme, true), + current: getThemePreference, + themes: [...THEMES], +});