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 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | 1x 6x 6x 6x 3x 6x 6x 6x 3x 3x 1x 1x 3x 1x 2x 2x | "use client";
import { useTheme } from "next-themes";
import { motion } from "framer-motion";
import { Sun, Moon, Monitor } from "lucide-react";
import { useEffect, useState } from "react";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
interface ThemeSwitcherProps {
variant?: "default" | "toggle";
}
export function ThemeSwitcher({ variant = "default" }: ThemeSwitcherProps) {
const { theme, setTheme, systemTheme } = useTheme();
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
// Handle current theme in toggle mode
const currentTheme = theme === "system" ? systemTheme : theme;
const isDark = currentTheme === "dark";
if (!mounted) {
return variant === "default" ? (
<div className="flex items-center gap-2 p-1 bg-gray-200 dark:bg-gray-800 rounded-xl animate-pulse w-[100px] h-8" />
) : (
<div className="w-16 h-8 bg-gray-300 dark:bg-gray-800 rounded-full" />
);
}
// ---------- VARIANT 1: DEFAULT TOOLTIP STYLE ----------
if (variant === "default") {
const themes = [
{ mode: "light", icon: <Sun className="w-4 h-4" />, label: "Light" },
{ mode: "dark", icon: <Moon className="w-4 h-4" />, label: "Dark" },
{ mode: "system", icon: <Monitor className="w-4 h-4" />, label: "System" },
];
return (
<TooltipProvider>
<div className="flex items-center p-0.5 bg-gray-100 dark:bg-gray-900 rounded-lg">
{themes.map(({ mode, icon, label }) => (
<Tooltip key={mode}>
<TooltipTrigger asChild>
<motion.button
onClick={() => setTheme(mode)}
aria-label={`Switch to ${label} theme`}
title={`Switch to ${label} theme`}
className={`flex items-center justify-center w-8 h-8 rounded-md transition-colors duration-200
${
theme === mode
? "bg-gray-300 dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm"
: "text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
}
`}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{icon}
</motion.button>
</TooltipTrigger>
<TooltipContent>
<p>{label}</p>
</TooltipContent>
</Tooltip>
))}
</div>
</TooltipProvider>
);
}
// ---------- VARIANT 2: TOGGLE STYLE ----------
return (
<div className="flex items-center gap-3">
<motion.button
onClick={() => setTheme(isDark ? "light" : "dark")}
aria-label={
isDark
? "Switch to light theme"
: "Switch to dark theme"
}
title={
isDark
? "Switch to light theme"
: "Switch to dark theme"
}
className="relative w-16 h-8 rounded-full bg-gray-300 dark:bg-gray-800 flex items-center px-1 shadow-lg border border-gray-400 dark:border-gray-700"
whileTap={{ scale: 0.95 }}
animate={{ scale: 1 }}
transition={{ type: "spring", stiffness: 300, damping: 20 }}
>
<motion.div
className="w-7 h-7 bg-white dark:bg-gray-950 rounded-full shadow-xl flex items-center justify-center"
initial={{ x: isDark ? 32 : 0 }}
animate={{ x: isDark ? 32 : 0 }}
transition={{
type: "spring",
stiffness: 250,
damping: 20,
mass: 0.5,
}}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9, rotate: 15 }}
>
<motion.div
key={currentTheme}
initial={{ rotate: isDark ? -180 : 180, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
transition={{ type: "spring", stiffness: 200, damping: 20 }}
>
{isDark ? (
<Moon className="w-4 h-4 text-gray-300" />
) : (
<Sun className="w-4 h-4 text-orange-500" />
)}
</motion.div>
</motion.div>
</motion.button>
</div>
);
}
export default ThemeSwitcher;
|