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:
co-authored by
Claude Opus 5.5
parent
5c40a64c42
commit
3bb0a5e4a9
+51
-24
@@ -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>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user