Skip to content

Commit 406d224

Browse files
Merge pull request #525 from jeetvasoya21/#521
Task Done
2 parents bbdc9e9 + 63b33f0 commit 406d224

3 files changed

Lines changed: 63 additions & 9 deletions

File tree

src/App.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
/* Global theme transition styles */
2+
:root {
3+
--transition-duration: 300ms;
4+
}
5+
6+
html.theme-transitioning,
7+
html.theme-transitioning * {
8+
transition: background-color var(--transition-duration) ease-in-out,
9+
color var(--transition-duration) ease-in-out,
10+
border-color var(--transition-duration) ease-in-out,
11+
box-shadow var(--transition-duration) ease-in-out !important;
12+
}
13+
114
#root {
215
max-width: 1280px;
316
margin: 0 auto;

src/components/Navbar.tsx

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { NavLink, Link } from "react-router-dom";
2-
import { useState, useContext } from "react";
2+
import { useState, useContext, useRef } from "react";
33
import { ThemeContext } from "../context/ThemeContext";
44
import { Moon, Sun, Menu, X} from "lucide-react";
55

66
const Navbar: React.FC = () => {
77
const [isOpen, setIsOpen] = useState(false);
8+
const [isThemeToggling, setIsThemeToggling] = useState(false);
9+
const themeToggleRef = useRef<HTMLButtonElement>(null);
810

911
const themeContext = useContext(ThemeContext);
1012
const userContext = useContext(UserContext);
@@ -14,6 +16,16 @@ const Navbar: React.FC = () => {
1416
const { toggleTheme, mode } = themeContext;
1517
const user = userContext?.user ?? null;
1618

19+
const handleThemeToggle = () => {
20+
setIsThemeToggling(true);
21+
toggleTheme();
22+
23+
// Reset animation state after transition
24+
setTimeout(() => {
25+
setIsThemeToggling(false);
26+
}, 300);
27+
};
28+
1729
const navLinkStyles = ({ isActive }: { isActive: boolean }) =>
1830
`px-4 py-2 rounded-xl text-sm lg:text-base font-semibold transition-all duration-300 ${
1931
isActive
@@ -67,14 +79,23 @@ const Navbar: React.FC = () => {
6779

6880
{/* Theme Toggle */}
6981
<button
70-
onClick={toggleTheme}
71-
className="ml-2 p-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
82+
ref={themeToggleRef}
83+
onClick={handleThemeToggle}
84+
className="ml-2 p-2 rounded-xl border border-gray-300 dark:border-gray-700 hover:bg-gray-100 dark:hover:bg-gray-800 transition-all duration-300"
7285
aria-label="Toggle Theme"
7386
>
7487
{mode === "dark" ? (
75-
<Sun className="h-5 w-5 text-yellow-400" />
88+
<Sun
89+
className={`h-5 w-5 text-yellow-400 transition-all duration-300 ${
90+
isThemeToggling ? 'rotate-180 scale-0' : 'rotate-0 scale-100'
91+
}`}
92+
/>
7693
) : (
77-
<Moon className="h-5 w-5 text-slate-700" />
94+
<Moon
95+
className={`h-5 w-5 text-slate-700 transition-all duration-300 ${
96+
isThemeToggling ? 'rotate-180 scale-0' : 'rotate-0 scale-100'
97+
}`}
98+
/>
7899
)}
79100
</button>
80101
</div>
@@ -84,14 +105,22 @@ const Navbar: React.FC = () => {
84105

85106
{/* Theme Toggle */}
86107
<button
87-
onClick={toggleTheme}
88-
className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
108+
onClick={handleThemeToggle}
109+
className="p-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-all duration-300"
89110
aria-label="Toggle Theme"
90111
>
91112
{mode === "dark" ? (
92-
<Sun className="h-5 w-5 text-yellow-400" />
113+
<Sun
114+
className={`h-5 w-5 text-yellow-400 transition-all duration-300 ${
115+
isThemeToggling ? 'rotate-180 scale-0' : 'rotate-0 scale-100'
116+
}`}
117+
/>
93118
) : (
94-
<Moon className="h-5 w-5 text-white" />
119+
<Moon
120+
className={`h-5 w-5 text-white transition-all duration-300 ${
121+
isThemeToggling ? 'rotate-180 scale-0' : 'rotate-0 scale-100'
122+
}`}
123+
/>
95124
)}
96125
</button>
97126

src/context/ThemeContext.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ interface ThemeContextType {
1010
export const ThemeContext = createContext<ThemeContextType | null>(null);
1111

1212
const THEME_STORAGE_KEY = 'theme';
13+
const TRANSITION_DURATION = 300; // milliseconds
1314

1415
const ThemeWrapper = ({ children }: { children: ReactNode }) => {
1516
const [mode, setMode] = useState<'light' | 'dark'>(() => {
@@ -19,12 +20,23 @@ const ThemeWrapper = ({ children }: { children: ReactNode }) => {
1920

2021
// Sync mode with <html> class and localStorage
2122
useEffect(() => {
23+
// Add transition class
24+
document.documentElement.classList.add('theme-transitioning');
25+
26+
// Apply theme change
2227
if (mode === 'dark') {
2328
document.documentElement.classList.add('dark');
2429
} else {
2530
document.documentElement.classList.remove('dark');
2631
}
2732
localStorage.setItem(THEME_STORAGE_KEY, mode);
33+
34+
// Remove transition class after animation completes
35+
const timer = setTimeout(() => {
36+
document.documentElement.classList.remove('theme-transitioning');
37+
}, TRANSITION_DURATION);
38+
39+
return () => clearTimeout(timer);
2840
}, [mode]);
2941

3042
const toggleTheme = () => {

0 commit comments

Comments
 (0)