~bigbes/lethe

ref: e920ae88160cd00850dcf0e5e4d876e2afe8600a lethe/web/src/lib/theme.ts -rw-r--r-- 1.5 KiB
e920ae88 — Eugene Blikh web: prune lethe_auth_failures log to the 5-min window on insert a month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Theme management — side-effect-free at import time.
// Call bootstrapTheme() once from main.tsx after the React root is mounted.

const MEDIA_QUERY = '(prefers-color-scheme: dark)'

function getOSDark(): boolean {
  return window.matchMedia(MEDIA_QUERY).matches
}

function applyTheme(theme: 'light' | 'dark'): void {
  document.documentElement.dataset['theme'] = theme
}

// Listener reference so the media-query change handler can check localStorage
// at call time (not at setup time).
function handleOSChange(e: MediaQueryListEvent): void {
  if (localStorage.getItem('theme') !== null) return
  applyTheme(e.matches ? 'dark' : 'light')
}

/**
 * bootstrapTheme — reads stored preference (or falls back to OS),
 * applies data-theme, and registers a media-query change listener.
 * Call exactly once from main.tsx.
 */
export function bootstrapTheme(): void {
  const stored = localStorage.getItem('theme')
  const isDark = stored !== null ? stored === 'dark' : getOSDark()
  applyTheme(isDark ? 'dark' : 'light')

  window.matchMedia(MEDIA_QUERY).addEventListener('change', handleOSChange)
}

/**
 * setTheme — explicit override.
 * null: clears override, re-syncs with current OS preference.
 * 'light' | 'dark': stores override, applies immediately.
 */
export function setTheme(theme: 'light' | 'dark' | null): void {
  if (theme === null) {
    localStorage.removeItem('theme')
    applyTheme(getOSDark() ? 'dark' : 'light')
  } else {
    localStorage.setItem('theme', theme)
    applyTheme(theme)
  }
}