diff --git a/src/components/ui/Navbar.tsx b/src/components/ui/Navbar.tsx index 400bd6f..973013b 100644 --- a/src/components/ui/Navbar.tsx +++ b/src/components/ui/Navbar.tsx @@ -1,9 +1,10 @@ import useMediaQuery from '@/hooks/useMediaQuery'; import useNavbar from '@/hooks/useNavbar'; import Link from 'next/link'; -import { FC, useEffect, useState } from 'react'; +import { FC } from 'react'; import { MdDarkMode, MdLightMode } from 'react-icons/md'; import { GiHamburgerMenu } from 'react-icons/gi'; +import useTheme from '@/hooks/useTheme'; const DesktopLinks: FC = () => { const { pages, currentURL } = useNavbar(); @@ -56,24 +57,6 @@ const MobileLinks: FC = () => { ); }; -const useTheme = () => { - const [theme, setTheme] = useState<'light' | 'dark'>('light'); - - const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - - useEffect(() => { - const savedTheme = localStorage.getItem('theme'); - if (prefersDarkMode && !savedTheme) { - setTheme('dark'); - localStorage.setItem('theme', 'dark'); - return; - } - setTheme(savedTheme as 'light' | 'dark'); - }, [prefersDarkMode, theme]); - - return { theme, setTheme }; -}; - const Navbar = () => { const isDesktopView = useMediaQuery('(min-width: 1024px)'); diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts new file mode 100644 index 0000000..6aa0595 --- /dev/null +++ b/src/hooks/useTheme.ts @@ -0,0 +1,31 @@ +import { useState, useEffect } from 'react'; +import useMediaQuery from './useMediaQuery'; + +/** + * A custom hook to manage the theme of the app. If a preferred theme is not set in + * localStorage, it will use what the user's browser prefers as determined by the + * prefers-color-scheme media query. If the user changes their preferred theme, it will be + * saved in localStorage and used in subsequent visits. + * + * @returns ThemeState.theme - The current theme of the app. + * @returns ThemeState.setTheme - A setter function to change the theme of the app. + */ +const useTheme = () => { + const [theme, setTheme] = useState<'light' | 'dark'>('light'); + + const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); + + useEffect(() => { + const savedTheme = localStorage.getItem('theme'); + if (prefersDarkMode && !savedTheme) { + setTheme('dark'); + localStorage.setItem('theme', 'dark'); + return; + } + setTheme(savedTheme as 'light' | 'dark'); + }, [prefersDarkMode, theme]); + + return { theme, setTheme }; +}; + +export default useTheme; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 5fb4f07..285c9ef 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -6,6 +6,7 @@ import { useEffect } from 'react'; import { themeChange } from 'theme-change'; import { Space_Grotesk } from 'next/font/google'; +import Head from 'next/head'; const spaceGrotesk = Space_Grotesk({ subsets: ['latin'], @@ -26,7 +27,12 @@ export default function App({ Component, pageProps }: AppProps) { } `} - + + + diff --git a/src/pages/_document.tsx b/src/pages/_document.tsx index 2117a3f..f5bcab6 100644 --- a/src/pages/_document.tsx +++ b/src/pages/_document.tsx @@ -22,10 +22,6 @@ export default function Document() { href="favicon/favicon-16x16.png" /> -