refactor: enhance theme switch component with loading state and improved animations

This commit is contained in:
Marco Pedone 2025-11-17 17:29:40 +01:00
parent b786d7741c
commit affc441cfc

View file

@ -1,9 +1,30 @@
import { motion, type Transition } from "framer-motion";
import { Moon, Sun } from "lucide-react";
import { useTheme } from "next-themes";
import { useEffect, useState } from "react";
import { cn } from "~/lib/utils";
export const ThemeSwitch = () => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return <ThemeSwitchSkeleton />;
}
return <ThemeSwitchContent />;
};
const ThemeSwitchSkeleton = () => (
<div
aria-hidden="true"
className="h-9 w-16 animate-pulse rounded-full bg-muted p-[5px] shadow-inner"
/>
);
const ThemeSwitchContent = () => {
const { theme, setTheme } = useTheme();
const spring: Transition = {
@ -25,14 +46,22 @@ export const ThemeSwitch = () => {
type="button"
>
<motion.div
animate={{ opacity: 1 }}
className={cn(
"flex size-7 items-center justify-center rounded-full bg-black",
theme === "light" ? "bg-yellow-300" : "bg-gray-800/60",
)}
initial={{ opacity: 0 }}
layout
transition={spring}
transition={{
opacity: { duration: 0.1, ease: "easeInOut", delay: 0.5 }, // Fast fade in
layout: spring, // Spring for position change
}}
>
<motion.div whileTap={{ rotate: 360 }}>
<motion.div
animate={{ rotate: theme === "light" ? 180 : 0 }}
transition={spring}
>
{theme === "light" ? (
<Sun className="size-5 fill-yellow-500 stroke-yellow-500" />
) : (