Skip to content

Commit 1d59acb

Browse files
committed
Fix: dark mode flicker and overflow issue
1 parent e20958a commit 1d59acb

File tree

5 files changed

+93
-35
lines changed

5 files changed

+93
-35
lines changed

index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@
1616
}
1717
})();
1818
</script>
19+
<script>
20+
// Initialize theme before React renders to prevent flicker
21+
(function() {
22+
const theme = localStorage.getItem('theme');
23+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
24+
25+
if (theme === 'dark' || (!theme && prefersDark)) {
26+
document.documentElement.classList.add('dark');
27+
} else {
28+
document.documentElement.classList.remove('dark');
29+
}
30+
})();
31+
</script>
1932
</head>
2033
<body>
2134
<div id="root"></div>

src/App.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,14 @@ import router from '@/routes';
44

55
const App = () => {
66
useEffect(() => {
7-
const unsubscribe = router.subscribe(() => {
8-
window.scrollTo(0, 0);
7+
let lastPathname = window.location.pathname;
8+
9+
const unsubscribe = router.subscribe((state) => {
10+
// Only scroll to top on pathname changes, not on query/hash changes
11+
if (state.location.pathname !== lastPathname) {
12+
window.scrollTo(0, 0);
13+
lastPathname = state.location.pathname;
14+
}
915
});
1016
const handleRedirect = () => {
1117
const redirectPath = sessionStorage.getItem('gh_redirect');

src/components/DeveloperTestimonials.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ const ReviewCard = ({
4949
/>
5050

5151
{/* Feedback Text */}
52-
<motion.p
53-
className="text-gray-700 dark:text-gray-300 mt-2"
52+
<motion.div
53+
className="text-gray-700 dark:text-gray-300 mt-2 flex-1 overflow-y-auto max-h-32 custom-scrollbar"
5454
variants={testimonialText}
5555
>
56-
{body}
57-
</motion.p>
56+
<p>{body}</p>
57+
</motion.div>
5858

5959
{/* User Info */}
6060
<div className="flex items-center mt-4 space-x-3 text-left">

src/components/shared/DarkModeToggle.tsx

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,51 @@ import { useState, useEffect } from 'react';
22
import { Moon, Sun } from 'lucide-react';
33

44
const DarkModeToggle = () => {
5-
const [isDarkMode, setIsDarkMode] = useState(false);
5+
// Initialize from the class that was already set in index.html
6+
const [isDarkMode, setIsDarkMode] = useState(() => {
7+
return document.documentElement.classList.contains('dark');
8+
});
69
const [isHovered, setIsHovered] = useState(false);
710

811
useEffect(() => {
12+
// Sync with localStorage and system preference on mount
913
const theme = localStorage.getItem('theme');
14+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
15+
1016
if (theme === 'dark') {
1117
document.documentElement.classList.add('dark');
1218
setIsDarkMode(true);
13-
} else {
19+
} else if (theme === 'light') {
1420
document.documentElement.classList.remove('dark');
1521
setIsDarkMode(false);
22+
} else {
23+
// If no preference saved, respect system preference
24+
if (prefersDark) {
25+
document.documentElement.classList.add('dark');
26+
setIsDarkMode(true);
27+
} else {
28+
document.documentElement.classList.remove('dark');
29+
setIsDarkMode(false);
30+
}
1631
}
32+
33+
// Listen for system theme changes (only if no manual preference is set)
34+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
35+
const handleThemeChange = (e: MediaQueryListEvent) => {
36+
// Only update if user hasn't set a manual preference
37+
if (!localStorage.getItem('theme')) {
38+
if (e.matches) {
39+
document.documentElement.classList.add('dark');
40+
setIsDarkMode(true);
41+
} else {
42+
document.documentElement.classList.remove('dark');
43+
setIsDarkMode(false);
44+
}
45+
}
46+
};
47+
48+
mediaQuery.addEventListener('change', handleThemeChange);
49+
return () => mediaQuery.removeEventListener('change', handleThemeChange);
1750
}, []);
1851

1952
const toggleDarkMode = () => {
@@ -34,24 +67,25 @@ const DarkModeToggle = () => {
3467
const translateX = isDarkMode ? trackWidth - thumbWidth - padding : padding;
3568

3669
return (
37-
<button
38-
onClick={toggleDarkMode}
39-
onMouseEnter={() => setIsHovered(true)}
40-
onMouseLeave={() => setIsHovered(false)}
41-
className="group relative inline-flex h-9 w-18 items-center rounded-full transition-all duration-500 focus:outline-none focus:ring-2 focus:ring-offset-1"
42-
style={{
43-
backgroundColor: isDarkMode ? '#1e293b' : '#bae6fd',
44-
boxShadow: isDarkMode
45-
? '0 6px 16px rgba(30, 41, 59, 0.4), inset 0 1px 2px rgba(255, 255, 255, 0.05)'
46-
: '0 6px 16px rgba(186, 230, 253, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.1)',
70+
<div className="flex items-center justify-center overflow-visible">
71+
<button
72+
onClick={toggleDarkMode}
73+
onMouseEnter={() => setIsHovered(true)}
74+
onMouseLeave={() => setIsHovered(false)}
75+
className="group relative inline-flex h-9 w-18 items-center rounded-full transition-all duration-500 focus:outline-none focus:ring-2 focus:ring-offset-1"
76+
style={{
77+
backgroundColor: isDarkMode ? '#1e293b' : '#bae6fd',
78+
boxShadow: isDarkMode
79+
? '0 6px 16px rgba(30, 41, 59, 0.4), inset 0 1px 2px rgba(255, 255, 255, 0.05)'
80+
: '0 6px 16px rgba(186, 230, 253, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.1)',
4781

48-
transform: isHovered ? 'scale(1.05)' : 'scale(1)',
49-
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
50-
}}
51-
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
52-
role="switch"
53-
aria-checked={isDarkMode}
54-
>
82+
transform: isHovered ? 'scale(1.05)' : 'scale(1)',
83+
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
84+
}}
85+
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
86+
role="switch"
87+
aria-checked={isDarkMode}
88+
>
5589
<div
5690
className="absolute inset-0 rounded-full overflow-hidden"
5791
style={{
@@ -143,6 +177,7 @@ const DarkModeToggle = () => {
143177
}}
144178
/>
145179
</button>
180+
</div>
146181
);
147182
};
148183

src/pages/News/NewsPage.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,17 @@ const NewsPage: React.FC = () => {
257257
>
258258
<div className="flex items-center justify-center mb-6">
259259
<Sparkles
260-
className="text-blue-500 mr-4 animate-pulse"
261-
size={32}
260+
className="text-blue-500 mr-2 sm:mr-4 animate-pulse"
261+
size={24}
262+
style={{ width: 24, height: 24 }}
262263
/>
263-
<h1 className="text-8xl font-bold font-Caveat text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-green-600">
264+
<h1 className="text-5xl sm:text-6xl md:text-7xl lg:text-8xl font-bold font-Caveat text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-green-600">
264265
NEWS
265266
</h1>
266267
<TrendingUp
267-
className="text-green-500 ml-4 animate-bounce"
268-
size={32}
268+
className="text-green-500 ml-2 sm:ml-4 animate-bounce"
269+
size={24}
270+
style={{ width: 24, height: 24 }}
269271
/>
270272
</div>
271273

@@ -381,11 +383,13 @@ const NewsPage: React.FC = () => {
381383
whileTap={{ scale: 0.95 }}
382384
>
383385
{cat}
384-
{activeCategory === cat && (
385-
<span className="ml-2 text-xs bg-white text-black bg-opacity-30 rounded-full px-2 py-1">
386-
{(postsByCategory[cat] || []).length}
387-
</span>
388-
)}
386+
<span className={`ml-2 text-xs rounded-full px-2 py-1 ${
387+
activeCategory === cat
388+
? 'bg-white text-black bg-opacity-30'
389+
: 'bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300'
390+
}`}>
391+
{(postsByCategory[cat] || []).length}
392+
</span>
389393
</motion.button>
390394
))}
391395
</div>

0 commit comments

Comments
 (0)