Skip to content
Closed
13 changes: 13 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
}
})();
</script>
<script>
// Initialize theme before React renders to prevent flicker
(function() {
const theme = localStorage.getItem('theme');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;

if (theme === 'dark' || (!theme && prefersDark)) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
})();
</script>
</head>
<body>
<div id="root"></div>
Expand Down
10 changes: 8 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ import router from '@/routes';

const App = () => {
useEffect(() => {
const unsubscribe = router.subscribe(() => {
window.scrollTo(0, 0);
let lastPathname = window.location.pathname;

const unsubscribe = router.subscribe((state) => {
// Only scroll to top on pathname changes, not on query/hash changes
if (state.location.pathname !== lastPathname) {
window.scrollTo(0, 0);
lastPathname = state.location.pathname;
}
});
const handleRedirect = () => {
const redirectPath = sessionStorage.getItem('gh_redirect');
Expand Down
8 changes: 4 additions & 4 deletions src/components/DeveloperTestimonials.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ const ReviewCard = ({
/>

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

{/* User Info */}
<div className="flex items-center mt-4 space-x-3 text-left">
Expand Down
220 changes: 120 additions & 100 deletions src/components/shared/DarkModeToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { Moon, Sun } from 'lucide-react';

const DarkModeToggle = () => {
// Initialize from the class that was already set in index.html
const [isDarkMode, setIsDarkMode] = useState(() => {
const theme = localStorage.getItem('theme');
const isDark = theme === 'dark';
if (isDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
return isDark;
const storedTheme = localStorage.getItem('theme');
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)',
).matches;
return storedTheme === 'dark' || (!storedTheme && prefersDark);
});
const [isHovered, setIsHovered] = useState(false);

useEffect(() => {
// Listen for system theme changes (only if no manual preference is set)
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleThemeChange = (e: MediaQueryListEvent) => {
// Only update if user hasn't set a manual preference
if (!localStorage.getItem('theme')) {
const shouldBeDark = e.matches;
if (shouldBeDark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
setIsDarkMode(shouldBeDark);
}
};

mediaQuery.addEventListener('change', handleThemeChange);
return () => mediaQuery.removeEventListener('change', handleThemeChange);
}, []);

const toggleDarkMode = () => {
if (isDarkMode) {
document.documentElement.classList.remove('dark');
Expand All @@ -32,115 +50,117 @@ const DarkModeToggle = () => {
const translateX = isDarkMode ? trackWidth - thumbWidth - padding : padding;

return (
<button
onClick={toggleDarkMode}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
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"
style={{
backgroundColor: isDarkMode ? '#1e293b' : '#bae6fd',
boxShadow: isDarkMode
? '0 6px 16px rgba(30, 41, 59, 0.4), inset 0 1px 2px rgba(255, 255, 255, 0.05)'
: '0 6px 16px rgba(186, 230, 253, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.1)',

transform: isHovered ? 'scale(1.05)' : 'scale(1)',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
role="switch"
aria-checked={isDarkMode}
>
<div
className="absolute inset-0 rounded-full overflow-hidden"
style={{
background: isDarkMode
? 'linear-gradient(135deg, rgba(148, 163, 184, 0.1) 0%, transparent 50%, rgba(71, 85, 105, 0.1) 100%)'
: 'linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, transparent 50%, rgba(56, 189, 248, 0.2) 100%)',
opacity: isHovered ? 1 : 0.6,
transition: 'opacity 0.3s ease',
}}
/>

<span
className="absolute rounded-full blur-md transition-all duration-500"
style={{
width: `${thumbWidth}px`,
height: `${thumbWidth}px`,
backgroundColor: isDarkMode ? '#818cf8' : '#fbbf24',
opacity: isHovered ? 0.5 : 0.3,
transform: `translateX(${translateX}px)`,
}}
/>

<span
className="relative z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-white overflow-hidden"
<div className="flex items-center justify-center overflow-visible">
<button
onClick={toggleDarkMode}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
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"
style={{
transform: `translateX(${translateX}px) ${isHovered ? 'scale(1.1)' : 'scale(1)'}`,
backgroundColor: isDarkMode ? '#1e293b' : '#bae6fd',
boxShadow: isDarkMode
? '0 3px 12px rgba(129, 140, 248, 0.5), 0 1px 4px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.7)'
: '0 3px 12px rgba(251, 191, 36, 0.5), 0 1px 4px rgba(0, 0, 0, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.8)',
transition: 'all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)',
? '0 6px 16px rgba(30, 41, 59, 0.4), inset 0 1px 2px rgba(255, 255, 255, 0.05)'
: '0 6px 16px rgba(186, 230, 253, 0.3), inset 0 1px 2px rgba(255, 255, 255, 0.1)',

transform: isHovered ? 'scale(1.05)' : 'scale(1)',
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
}}
aria-label={isDarkMode ? 'Switch to light mode' : 'Switch to dark mode'}
role="switch"
aria-checked={isDarkMode}
>
<div
className="absolute inset-0"
className="absolute inset-0 rounded-full overflow-hidden"
style={{
background: isDarkMode
? 'radial-gradient(circle, rgba(129, 140, 248, 0.3) 0%, transparent 70%)'
: 'radial-gradient(circle, rgba(251, 191, 36, 0.3) 0%, transparent 70%)',
? 'linear-gradient(135deg, rgba(148, 163, 184, 0.1) 0%, transparent 50%, rgba(71, 85, 105, 0.1) 100%)'
: 'linear-gradient(135deg, rgba(255, 255, 255, 0.3) 0%, transparent 50%, rgba(56, 189, 248, 0.2) 100%)',
opacity: isHovered ? 1 : 0.6,
transition: 'opacity 0.3s ease',
}}
/>

<Sun
className="absolute transition-all duration-500"
size={16}
<span
className="absolute rounded-full blur-md transition-all duration-500"
style={{
color: '#f59e0b',
opacity: isDarkMode ? 0 : 1,
transform: isDarkMode
? 'rotate(360deg) scale(0.3)'
: `rotate(0deg) scale(1) ${isHovered ? 'rotate(30deg)' : ''}`,
filter: isDarkMode
? 'blur(2px)'
: 'blur(0px) drop-shadow(0 0 6px rgba(245, 158, 11, 0.5))',
transition: 'all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)',
width: `${thumbWidth}px`,
height: `${thumbWidth}px`,
backgroundColor: isDarkMode ? '#818cf8' : '#fbbf24',
opacity: isHovered ? 0.5 : 0.3,
transform: `translateX(${translateX}px)`,
}}
/>

<Moon
className="absolute transition-all duration-500"
size={16}
<span
className="relative z-10 inline-flex h-7 w-7 items-center justify-center rounded-full bg-white overflow-hidden"
style={{
color: '#818cf8',
opacity: isDarkMode ? 1 : 0,
transform: isDarkMode
? 'rotate(0deg) scale(1)'
: 'rotate(-360deg) scale(0.3)',
filter: isDarkMode
? 'drop-shadow(0 0 6px rgba(129, 140, 248, 0.5))'
: 'blur(2px)',
transform: `translateX(${translateX}px) ${isHovered ? 'scale(1.1)' : 'scale(1)'}`,
boxShadow: isDarkMode
? '0 3px 12px rgba(129, 140, 248, 0.5), 0 1px 4px rgba(0, 0, 0, 0.2), inset 0 1px 1px rgba(255, 255, 255, 0.7)'
: '0 3px 12px rgba(251, 191, 36, 0.5), 0 1px 4px rgba(0, 0, 0, 0.1), inset 0 1px 1px rgba(255, 255, 255, 0.8)',
transition: 'all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)',
}}
/>
</span>
>
<div
className="absolute inset-0"
style={{
background: isDarkMode
? 'radial-gradient(circle, rgba(129, 140, 248, 0.3) 0%, transparent 70%)'
: 'radial-gradient(circle, rgba(251, 191, 36, 0.3) 0%, transparent 70%)',
}}
/>

<Sun
className="absolute left-2 top-1/2 -translate-y-1/2 transition-all duration-500"
size={12}
style={{
color: 'rgba(255, 255, 255, 0.9)',
opacity: isDarkMode ? 0.2 : 0.7,
}}
/>
<Moon
className="absolute right-2 top-1/2 -translate-y-1/2 transition-all duration-500"
size={12}
style={{
color: 'rgba(255, 255, 255, 0.9)',
opacity: isDarkMode ? 0.7 : 0.2,
}}
/>
</button>
<Sun
className="absolute transition-all duration-500"
size={16}
style={{
color: '#f59e0b',
opacity: isDarkMode ? 0 : 1,
transform: isDarkMode
? 'rotate(360deg) scale(0.3)'
: `rotate(0deg) scale(1) ${isHovered ? 'rotate(30deg)' : ''}`,
filter: isDarkMode
? 'blur(2px)'
: 'blur(0px) drop-shadow(0 0 6px rgba(245, 158, 11, 0.5))',
transition: 'all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)',
}}
/>

<Moon
className="absolute transition-all duration-500"
size={16}
style={{
color: '#818cf8',
opacity: isDarkMode ? 1 : 0,
transform: isDarkMode
? 'rotate(0deg) scale(1)'
: 'rotate(-360deg) scale(0.3)',
filter: isDarkMode
? 'drop-shadow(0 0 6px rgba(129, 140, 248, 0.5))'
: 'blur(2px)',
transition: 'all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1)',
}}
/>
</span>

<Sun
className="absolute left-2 top-1/2 -translate-y-1/2 transition-all duration-500"
size={12}
style={{
color: 'rgba(255, 255, 255, 0.9)',
opacity: isDarkMode ? 0.2 : 0.7,
}}
/>
<Moon
className="absolute right-2 top-1/2 -translate-y-1/2 transition-all duration-500"
size={12}
style={{
color: 'rgba(255, 255, 255, 0.9)',
opacity: isDarkMode ? 0.7 : 0.2,
}}
/>
</button>
</div>
);
};

Expand Down
26 changes: 16 additions & 10 deletions src/pages/News/NewsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -272,15 +272,17 @@ const NewsPage: React.FC = () => {
>
<div className="flex items-center justify-center mb-6">
<Sparkles
className="text-blue-500 mr-4 animate-pulse"
size={32}
className="text-blue-500 mr-2 sm:mr-4 animate-pulse"
size={24}
style={{ width: 24, height: 24 }}
/>
<h1 className="p-4 text-8xl font-bold font-Caveat text-transparent bg-clip-text bg-gradient-to-r from-blue-600 to-green-600">
<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">
NEWS
</h1>
<TrendingUp
className="text-green-500 ml-4 animate-bounce"
size={32}
className="text-green-500 ml-2 sm:ml-4 animate-bounce"
size={24}
style={{ width: 24, height: 24 }}
/>
</div>

Expand Down Expand Up @@ -400,11 +402,15 @@ const NewsPage: React.FC = () => {
whileTap={{ scale: 0.95 }}
>
{cat}
{activeCategory === cat && (
<span className="ml-2 text-xs bg-white text-black bg-opacity-30 rounded-full px-2 py-1">
{(postsByCategory[cat] || []).length}
</span>
)}
<span
className={`ml-2 text-xs rounded-full px-2 py-1 ${
activeCategory === cat
? 'bg-white text-black bg-opacity-30'
: 'bg-gray-200 dark:bg-gray-600 text-gray-700 dark:text-gray-300'
}`}
>
{(postsByCategory[cat] || []).length}
</span>
</motion.button>
))}
</div>
Expand Down