11'use client'
22
3- import { Fragment , useEffect , useRef } from 'react'
3+ import { Fragment , useEffect , useRef , useState } from 'react'
44import Image from 'next/image'
55import Link from 'next/link'
66import { usePathname } from 'next/navigation'
77import { Popover , Transition } from '@headlessui/react'
88import clsx from 'clsx'
9- import { motion } from 'framer-motion'
9+ import { AnimatePresence , motion , useReducedMotion } from 'framer-motion'
1010
1111import { Container } from '@/components/shared/Container'
1212
@@ -183,6 +183,29 @@ function DesktopNavigation(props) {
183183}
184184
185185function ModeToggle ( ) {
186+ const [ mounted , setMounted ] = useState ( false )
187+ const [ isDarkMode , setIsDarkMode ] = useState ( false )
188+ const prefersReducedMotion = useReducedMotion ( )
189+ const iconTransition = prefersReducedMotion
190+ ? { duration : 0 }
191+ : { duration : 0.5 , ease : 'easeInOut' }
192+
193+ useEffect ( ( ) => {
194+ setMounted ( true )
195+ setIsDarkMode ( document . documentElement . classList . contains ( 'dark' ) )
196+
197+ const observer = new MutationObserver ( ( ) => {
198+ setIsDarkMode ( document . documentElement . classList . contains ( 'dark' ) )
199+ } )
200+
201+ observer . observe ( document . documentElement , {
202+ attributes : true ,
203+ attributeFilter : [ 'class' ] ,
204+ } )
205+
206+ return ( ) => observer . disconnect ( )
207+ } , [ ] )
208+
186209 function disableTransitionsTemporarily ( ) {
187210 document . documentElement . classList . add ( '[&_*]:!transition-none' )
188211 window . setTimeout ( ( ) => {
@@ -204,15 +227,42 @@ function ModeToggle() {
204227 }
205228 }
206229
230+ if ( ! mounted ) {
231+ return (
232+ < div className = "h-10 w-10 rounded-full bg-white/90 ring-1 ring-black/10 dark:bg-zinc-800/90 dark:ring-white/10" />
233+ )
234+ }
235+
207236 return (
208237 < button
209238 type = "button"
210239 aria-label = "Toggle dark mode"
211- className = "group rounded-full bg-white/90 px-3 py-2 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
240+ className = "group relative flex h-10 w-10 items-center justify-center rounded-full bg-white/90 shadow-lg shadow-zinc-800/5 ring-1 ring-black/10 hover:ring-black/20 backdrop-blur transition dark:bg-zinc-800/90 dark:ring-white/10 dark:hover:ring-white/20"
212241 onClick = { toggleMode }
213242 >
214- < SunIcon className = "h-6 w-6 fill-zinc-100 stroke-zinc-500 transition group-hover:fill-zinc-200 group-hover:stroke-zinc-700 dark:hidden [@media(prefers-color-scheme:dark)]:fill-teal-50 [@media(prefers-color-scheme:dark)]:stroke-yellow-400 [@media(prefers-color-scheme:dark)]:group-hover:fill-teal-50 [@media(prefers-color-scheme:dark)]:group-hover:stroke-yellow-500" />
215- < MoonIcon className = "hidden h-6 w-6 fill-zinc-700 stroke-zinc-500 transition dark:block [@media(prefers-color-scheme:dark)]:group-hover:stroke-zinc-400 [@media_not_(prefers-color-scheme:dark)]:fill-teal-400/10 [@media_not_(prefers-color-scheme:dark)]:stroke-teal-500" />
243+ < AnimatePresence mode = "wait" initial = { false } >
244+ { isDarkMode ? (
245+ < motion . div
246+ key = "moon"
247+ initial = { { opacity : 0 , rotate : - 90 , scale : 0.5 } }
248+ animate = { { opacity : 1 , rotate : 0 , scale : 1 } }
249+ exit = { { opacity : 0 , rotate : 90 , scale : 0.5 } }
250+ transition = { iconTransition }
251+ >
252+ < MoonIcon className = "h-6 w-6 fill-zinc-700 stroke-zinc-500 transition group-hover:stroke-zinc-400 [@media_not_(prefers-color-scheme:dark)]:fill-teal-400/10 [@media_not_(prefers-color-scheme:dark)]:stroke-teal-500" />
253+ </ motion . div >
254+ ) : (
255+ < motion . div
256+ key = "sun"
257+ initial = { { opacity : 0 , rotate : 90 , scale : 0.5 } }
258+ animate = { { opacity : 1 , rotate : 0 , scale : 1 } }
259+ exit = { { opacity : 0 , rotate : - 90 , scale : 0.5 } }
260+ transition = { iconTransition }
261+ >
262+ < SunIcon className = "h-6 w-6 fill-zinc-100 stroke-zinc-500 transition group-hover:fill-zinc-200 group-hover:stroke-zinc-700 [@media(prefers-color-scheme:dark)]:fill-teal-50 [@media(prefers-color-scheme:dark)]:stroke-yellow-400 [@media(prefers-color-scheme:dark)]:group-hover:fill-teal-50 [@media(prefers-color-scheme:dark)]:group-hover:stroke-yellow-500" />
263+ </ motion . div >
264+ ) }
265+ </ AnimatePresence >
216266 </ button >
217267 )
218268}
0 commit comments