feat: redesign site as a modern developer portfolio

- New home page: hero, stats, about, focus areas, stack, projects, contact
- Light/dark theme with system default and persisted toggle
- Content centralised in data/profile.js
- Restyle /rates to match; remove it from nav and projects
- Afriex: log a clear error now that its API requires a device id

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jesse Egbosionu
2026-10-24 21:19:22 +02:00
co-authored by Claude Opus 5.5
parent 5c40a64c42
commit 3bb0a5e4a9
17 changed files with 1873 additions and 872 deletions
+14 -8
View File
@@ -1,27 +1,33 @@
import { useState, useEffect } from 'react';
import { profile } from '../data/profile';
const names = ['👨🏿‍💼 Jesse Egbosionu', '👨🏿‍💻 jesseinit', '😎 jessewithit'];
const names = profile.handles;
export default function AnimatedName() {
const [currentIndex, setCurrentIndex] = useState(0);
const [isAnimating, setIsAnimating] = useState(false);
useEffect(() => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
let timeout;
const interval = setInterval(() => {
setIsAnimating(true);
setTimeout(() => {
timeout = setTimeout(() => {
setCurrentIndex((prevIndex) => (prevIndex + 1) % names.length);
setIsAnimating(false);
}, 500); // Half of animation duration for smooth transition
}, 3000); // Change name every 3 seconds
}, 350);
}, 3000);
return () => clearInterval(interval);
return () => {
clearInterval(interval);
clearTimeout(timeout);
};
}, []);
return (
<span className="animated-name-wrapper">
<span className={`highlight animated-name ${isAnimating ? 'fade-out' : 'fade-in'}`}>
<span className="animated-name" aria-label={names[0]}>
<span aria-hidden="true" className={isAnimating ? 'is-leaving' : 'is-entering'}>
{names[currentIndex]}
</span>
</span>