Sifa professional network frontend (Next.js, React, TailwindCSS)
sifa.id/
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}