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
+51 -24
View File
@@ -1,31 +1,58 @@
import { useEffect, useState } from 'react';
import Link from 'next/link';
import ThemeToggle from './ThemeToggle';
import Icon from './Icon';
const links = [
{ href: '/#about', label: 'About' },
{ href: '/#stack', label: 'Stack' },
{ href: '/#projects', label: 'Projects' },
];
export default function Header() {
const [scrolled, setScrolled] = useState(false);
const [open, setOpen] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 8);
onScroll();
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
export default function Header({ showNavLinks = true }) {
return (
<header className="header">
<nav className="navbar">
<div className="logo">
<Link href="/">
<span className="logo-text">JE</span>
</Link>
<header className={`header ${scrolled ? 'is-scrolled' : ''} ${open ? 'is-open' : ''}`}>
<nav className="navbar shell">
<Link href="/" className="logo" onClick={() => setOpen(false)}>
<span className="logo-mark">je</span>
<span className="logo-text">
jesseinit<span className="logo-dot">.dev</span>
</span>
</Link>
<div className="nav-links">
{links.map((link) => (
<Link key={link.href} href={link.href} className="nav-link" onClick={() => setOpen(false)}>
{link.label}
</Link>
))}
</div>
<div className="nav-actions">
<ThemeToggle />
<a href="/#contact" className="button button-primary button-small nav-cta">
Contact
</a>
<button
type="button"
className="icon-button nav-menu"
aria-label={open ? 'Close menu' : 'Open menu'}
aria-expanded={open}
onClick={() => setOpen(!open)}
>
<Icon name={open ? 'close' : 'menu'} />
</button>
</div>
{showNavLinks && (
<div className="nav-links">
<Link href="/" className="nav-link">
Home
</Link>
<Link href="/rates" className="nav-link cta">
Rates
</Link>
</div>
)}
{!showNavLinks && (
<div className="nav-links">
<a href="mailto:me@jesseinit.dev" className="nav-link cta">
Get in touch
</a>
</div>
)}
</nav>
</header>
);