Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 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 | 1x 1x 1x 66x 66x 21x 21x 73x 73x 73x 21x 21x 21x 21x 21x 2x 2x 21x 21x 21x 1x 1x 1x 1x 1x 1x 1x 1x 1x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 21x 66x 66x 4x 4x 4x 4x 4x 66x 66x 66x 66x 66x 66x | import { useState, useEffect } from 'react';
import { THEMES } from '@/types';
/**
* Custom hook for managing theme state synchronized with astro-themes
* @returns An object containing the current theme and a toggle function
*/
export const useTheme = () => {
const [theme, setTheme] = useState<THEMES.LIGHT | THEMES.DARK | null>(null);
useEffect(() => {
if (typeof window === 'undefined') return;
const syncTheme = () => {
const themeAttr = document.documentElement.getAttribute('data-theme');
// resolved theme is always 'light' or 'dark'
setTheme(themeAttr === THEMES.DARK ? THEMES.DARK : THEMES.LIGHT);
};
// Sync immediately + retries to cover hydration timing
syncTheme();
queueMicrotask(syncTheme);
const rafId = requestAnimationFrame(syncTheme);
const timeoutId = setTimeout(syncTheme, 50);
// Listen for theme changes from astro-themes
const handleThemeChange = () => {
syncTheme();
};
window.addEventListener('theme-changed', handleThemeChange);
document.addEventListener('astro-themes:change', handleThemeChange);
// Also observe data-theme attribute changes as a fallback
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (
mutation.type === 'attributes' &&
mutation.attributeName === 'data-theme'
) {
syncTheme();
break;
}
}
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme'],
});
return () => {
cancelAnimationFrame(rafId);
clearTimeout(timeoutId);
window.removeEventListener('theme-changed', handleThemeChange);
document.removeEventListener('astro-themes:change', handleThemeChange);
observer.disconnect();
};
}, []);
const toggleTheme = () => {
if (theme === null) return;
const next = theme === THEMES.DARK ? THEMES.LIGHT : THEMES.DARK;
// Tell astro-themes to set & persist the preference
document.dispatchEvent(new CustomEvent('set-theme', { detail: next }));
// Optimistic update for instant feedback
setTheme(next);
};
return {
theme,
toggleTheme,
isDarkMode: theme === THEMES.DARK,
};
};
|