Skip to content

Commit 0dd02f4

Browse files
committed
theme toggle animation on toggle
1 parent c759764 commit 0dd02f4

File tree

1 file changed

+22
-12
lines changed

1 file changed

+22
-12
lines changed

src/components/theme-toggle.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const DARK_MODE_SCALE = 0.5;
1717
export const ThemeToggle = ({ className = "" }: { className?: string }) => {
1818
const { theme, setTheme } = useTheme();
1919
const [mounted, setMounted] = useState(false);
20+
const [hasInteracted, setHasInteracted] = useState(false);
2021

2122
// useEffect only runs on the client, so now we can safely show the UI
2223
useEffect(() => {
@@ -48,14 +49,19 @@ export const ThemeToggle = ({ className = "" }: { className?: string }) => {
4849

4950
const isDark = theme === "dark";
5051

52+
const handleClick = () => {
53+
setHasInteracted(true);
54+
setTheme(isDark ? "light" : "dark");
55+
};
56+
5157
return (
5258
<button
5359
aria-label={`Switch to ${isDark ? "light" : "dark"} mode`}
5460
className={cn(
5561
"cursor-pointer rounded-full transition-all duration-300 active:scale-95",
5662
className
5763
)}
58-
onClick={() => setTheme(isDark ? "light" : "dark")}
64+
onClick={handleClick}
5965
type="button"
6066
>
6167
<svg
@@ -72,15 +78,23 @@ export const ThemeToggle = ({ className = "" }: { className?: string }) => {
7278
x: isDark ? DARK_MODE_CLIP_X : 0,
7379
}}
7480
d="M0-5h30a1 1 0 0 0 9 13v24H0Z"
75-
transition={{ ease: "easeInOut", duration: 0.35 }}
81+
transition={
82+
hasInteracted
83+
? { ease: "easeInOut", duration: 0.35 }
84+
: { duration: 0 }
85+
}
7686
/>
7787
</clipPath>
7888
<g clipPath="url(#skiper-btn-2)">
7989
<motion.circle
8090
animate={{ r: isDark ? DARK_MODE_RADIUS : LIGHT_MODE_RADIUS }}
8191
cx="16"
8292
cy="16"
83-
transition={{ ease: "easeInOut", duration: 0.35 }}
93+
transition={
94+
hasInteracted
95+
? { ease: "easeInOut", duration: 0.35 }
96+
: { duration: 0 }
97+
}
8498
/>
8599
<motion.g
86100
animate={{
@@ -90,7 +104,11 @@ export const ThemeToggle = ({ className = "" }: { className?: string }) => {
90104
}}
91105
stroke="currentColor"
92106
strokeWidth="1.5"
93-
transition={{ ease: "easeInOut", duration: 0.35 }}
107+
transition={
108+
hasInteracted
109+
? { ease: "easeInOut", duration: 0.35 }
110+
: { duration: 0 }
111+
}
94112
>
95113
<path d="M16 5.5v-4" />
96114
<path d="M16 30.5v-4" />
@@ -106,11 +124,3 @@ export const ThemeToggle = ({ className = "" }: { className?: string }) => {
106124
</button>
107125
);
108126
};
109-
110-
/**
111-
* Theme Toggle Animation — React + Framer Motion Recreation
112-
* Inspired by and adapted from https://toggles.dev/ (Open Source CSS Theme Toggles by Alfie Jones)
113-
* This implementation is rebuilt in React and Framer Motion, avoiding external toggle packages.
114-
*
115-
* Attribution: https://toggles.dev/
116-
*/

0 commit comments

Comments
 (0)