comparison 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
comparison
equal deleted inserted replaced
255:5ec271d612ae 256:30c2196d03d4
1 const THEME_STORAGE_KEY = 'theme-preference'; 1 const THEME_STORAGE_KEY = 'theme-preference';
2 // Get stored preference or detect system preference 2 const THEMES = ['auto', 'paper', 'ink', 'playful'];
3 function getThemePreference() 3 const THEME_LABELS = {
4 { 4 auto: 'Auto',
5 const stored = localStorage.getItem(THEME_STORAGE_KEY); 5 paper: 'Paper',
6 if (stored) { 6 ink: 'Ink',
7 return stored; 7 playful: 'Playful',
8 } 8 };
9 return 'auto'; 9
10 function normalizeTheme(value) {
11 if (value === 'light') return 'paper';
12 if (value === 'dark') return 'ink';
13 return THEMES.includes(value) ? value : 'auto';
10 } 14 }
11 15
12 function applyTheme(preference) 16 function getThemePreference() {
13 { 17 return normalizeTheme(localStorage.getItem(THEME_STORAGE_KEY));
14 const root = document.documentElement;
15
16 if (preference === 'light') {
17 root.classList.remove('dark', 'auto');
18 root.classList.add('light-mode');
19 } else if (preference === 'dark') {
20 root.classList.remove('light-mode', 'auto');
21 root.classList.add('dark');
22 } else {
23 // Auto - remove manual overrides and let CSS media query handle it
24 root.classList.remove('light-mode', 'dark');
25 root.classList.add('auto');
26 }
27 } 18 }
28 19
29 (function() { 20 function updateThemeColor() {
30 const currentPreference = getThemePreference(); 21 const probe = document.createElement('span');
31 applyTheme(currentPreference); 22 probe.style.background = 'var(--zen-color-canvas)';
32 })(); 23 document.body.append(probe);
24 const color = getComputedStyle(probe).backgroundColor;
25 probe.remove();
26 document.querySelector('meta[name="theme-color"]')
27 ?.setAttribute('content', color);
28 }
33 29
34 /* Toggle lights auto and dark */ 30 function updateThemeControl(preference) {
35 const toggle = document.querySelector('#themeToggle'); 31 const toggle = document.querySelector('#themeToggle');
36 toggle.addEventListener('click', function() { 32 const label = document.querySelector('#themeName');
37 let preference = getThemePreference(); 33 if (label) label.textContent = THEME_LABELS[preference];
34 toggle?.setAttribute(
35 'aria-label',
36 `Change site theme. Current theme: ${THEME_LABELS[preference]}`,
37 );
38 }
38 39
39 // Cycle through: auto -> light -> dark -> auto 40 function applyTheme(value, persist = false) {
40 if (preference === 'auto') { 41 const preference = normalizeTheme(value);
41 preference = 'light'; 42 const root = document.documentElement;
42 } else if (preference === 'light') { 43 root.classList.remove('light-mode', 'dark', 'auto');
43 preference = 'dark'; 44 if (preference === 'auto') delete root.dataset.zenTheme;
44 } else { 45 else root.dataset.zenTheme = preference;
45 preference = 'auto'; 46 if (persist) localStorage.setItem(THEME_STORAGE_KEY, preference);
46 } 47 updateThemeControl(preference);
48 requestAnimationFrame(updateThemeColor);
49 document.dispatchEvent(new CustomEvent('zen-theme-change', {
50 detail: { theme: preference },
51 }));
52 return preference;
53 }
47 54
48 localStorage.setItem(THEME_STORAGE_KEY, preference); 55 const currentPreference = applyTheme(getThemePreference());
49 applyTheme(preference); 56 if (localStorage.getItem(THEME_STORAGE_KEY) !== currentPreference) {
57 localStorage.setItem(THEME_STORAGE_KEY, currentPreference);
58 }
59
60 document.querySelector('#themeToggle')?.addEventListener('click', () => {
61 const current = getThemePreference();
62 const next = THEMES[(THEMES.indexOf(current) + 1) % THEMES.length];
63 applyTheme(next, true);
50 }); 64 });
65
66 matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
67 if (getThemePreference() === 'auto') updateThemeColor();
68 });
69
70 window.MrJuneJuneTheme = Object.freeze({
71 apply: theme => applyTheme(theme, true),
72 current: getThemePreference,
73 themes: [...THEMES],
74 });