Sifa professional network frontend (Next.js, React, TailwindCSS) sifa.id/
at main 61 lines 1.8 kB view raw
1'use client'; 2 3import { useTheme } from 'next-themes'; 4import { Moon, Sun } from '@phosphor-icons/react'; 5import { useLayoutEffect, useState } from 'react'; 6import { useTranslations } from 'next-intl'; 7import { cn } from '@/lib/utils'; 8 9interface ThemeToggleProps { 10 className?: string; 11} 12 13export function ThemeToggle({ className }: ThemeToggleProps) { 14 const { theme, setTheme } = useTheme(); 15 const [mounted, setMounted] = useState(false); 16 const t = useTranslations('common'); 17 18 // Required for hydration mismatch prevention -- theme is unknown during SSR 19 /* eslint-disable react-hooks/set-state-in-effect */ 20 useLayoutEffect(() => { 21 setMounted(true); 22 }, []); 23 /* eslint-enable react-hooks/set-state-in-effect */ 24 25 if (!mounted) { 26 return ( 27 <button 28 className={cn( 29 'inline-flex h-9 w-9 items-center justify-center rounded-md border border-border text-muted-foreground', 30 className, 31 )} 32 disabled 33 aria-hidden="true" 34 > 35 <span className="h-5 w-5" /> 36 </button> 37 ); 38 } 39 40 const isDark = theme === 'dark'; 41 42 return ( 43 <button 44 type="button" 45 onClick={() => setTheme(isDark ? 'light' : 'dark')} 46 className={cn( 47 'inline-flex h-9 w-9 items-center justify-center rounded-md border border-border text-muted-foreground transition-colors', 48 'hover:bg-accent hover:text-foreground', 49 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2', 50 className, 51 )} 52 aria-label={isDark ? t('switchToLight') : t('switchToDark')} 53 > 54 {isDark ? ( 55 <Sun className="h-5 w-5" weight="regular" aria-hidden="true" /> 56 ) : ( 57 <Moon className="h-5 w-5" weight="regular" aria-hidden="true" /> 58 )} 59 </button> 60 ); 61}