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 100x 100x 38x 38x 106x 106x 106x 38x 38x 38x 38x 38x 2x 2x 38x 38x 38x 1x 1x 1x 1x 1x 1x 1x 1x 1x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 38x 100x 100x 4x 4x 4x 4x 4x 100x 100x 100x 100x 100x 100x | 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,
};
};
|