-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathuseTheme.mjs
More file actions
107 lines (91 loc) · 2.84 KB
/
useTheme.mjs
File metadata and controls
107 lines (91 loc) · 2.84 KB
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { useState, useEffect, useCallback } from 'react';
const THEME_STORAGE_KEY = 'theme';
const THEME_PREFERENCES = new Set(['system', 'light', 'dark']);
/**
*
*/
const getSystemTheme = () =>
matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
/**
*
*/
const getStoredThemePreference = () => {
try {
const storedTheme = localStorage.getItem(THEME_STORAGE_KEY);
return THEME_PREFERENCES.has(storedTheme) ? storedTheme : null;
} catch {
return null;
}
};
/**
*
*/
const setStoredThemePreference = themePreference => {
try {
localStorage.setItem(THEME_STORAGE_KEY, themePreference);
} catch {
// Ignore storage failures and keep non-persistent in-memory preference.
}
};
/**
* Applies a theme preference to the document.
*
* The persisted preference can be 'system', but the applied document theme is
* always resolved to either 'light' or 'dark'.
*
* @param {'system'|'light'|'dark'} themePreference - Theme preference.
*/
const applyThemePreference = themePreference => {
const resolvedTheme =
themePreference === 'system' ? getSystemTheme() : themePreference;
document.documentElement.setAttribute('data-theme', resolvedTheme);
document.documentElement.style.colorScheme = resolvedTheme;
};
/**
* A React hook for managing the application's theme preference.
*/
export const useTheme = () => {
const [themePreference, setThemePreferenceState] = useState('system');
useEffect(() => {
// Use persisted preference if available, otherwise default to system.
const initialPreference = getStoredThemePreference() || 'system';
applyThemePreference(initialPreference);
setThemePreferenceState(initialPreference);
}, []);
/**
* Keep the resolved document theme in sync with system changes
* whenever the preference is set to 'system'.
*/
useEffect(() => {
if (themePreference !== 'system') {
return;
}
const mediaQueryList = matchMedia('(prefers-color-scheme: dark)');
/**
*
*/
const handleSystemThemeChange = () => applyThemePreference('system');
if ('addEventListener' in mediaQueryList) {
mediaQueryList.addEventListener('change', handleSystemThemeChange);
return () => {
mediaQueryList.removeEventListener('change', handleSystemThemeChange);
};
}
mediaQueryList.addListener(handleSystemThemeChange);
return () => {
mediaQueryList.removeListener(handleSystemThemeChange);
};
}, [themePreference]);
/**
* Updates the theme preference and applies it immediately.
*/
const setThemePreference = useCallback(nextPreference => {
if (!THEME_PREFERENCES.has(nextPreference)) {
return;
}
setThemePreferenceState(nextPreference);
setStoredThemePreference(nextPreference);
applyThemePreference(nextPreference);
}, []);
return [themePreference, setThemePreference];
};