Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | 3x 3x 3x 20x 3x 20x 80x | 'use client';
import { m, stagger } from 'motion/react';
import Link from 'next/link';
import { useEffect } from 'react';
import type { JSX } from 'react';
import { Statement } from './Statement';
import { At, File, GitHub, LinkedIn } from '@/components/icons';
const [HIDE, SHOW] = ['0', '1'];
const links = [
[{ svg: At }, 'Email', 'mailto:joshua@geschwendt.com'],
[{ svg: File }, 'Resume', '/resume'],
[{ svg: GitHub }, 'GitHub', 'https://github.com/jgeschwendt'],
[{ svg: LinkedIn }, 'LinkedIn', 'https://www.linkedin.com/in/jgeschwendt'],
] as const;
// Module scope so the entrance plays once per session, not again on every return navigation.
let hasPlayed = false;
export function Main(): JSX.Element {
useEffect(() => {
hasPlayed = true;
}, []);
return (
<main className="pointer-events-none flex min-h-dvh items-center justify-center px-4 py-16">
<m.div
animate={SHOW}
className="pointer-events-auto flex flex-col items-center"
initial={hasPlayed ? SHOW : HIDE}
variants={{
[HIDE]: { opacity: 0 },
[SHOW]: {
opacity: 1,
transition: { delayChildren: stagger(2) },
},
}}
>
<m.svg
className="w-48 stroke-white"
strokeLinecap="square"
strokeLinejoin="miter"
strokeWidth={0.5}
viewBox="0 0 48 48"
xmlns="http://www.w3.org/2000/svg"
>
<m.path
d="M13.25 20C13.25 14.6988 17.4808 10.3858 22.75 10.2531V29.7469C17.4808 29.6142 13.25 25.3012 13.25 20ZM25.25 10.25H32.75V30C32.75 35.3012 28.5192 39.6142 23.25 39.7469V32.2363C24.375 32.1119 25.25 31.1581 25.25 30V10.25Z"
transition={{
default: { delay: 0.25, duration: 1.75, ease: 'easeIn' },
fill: { delay: 1.25, duration: 0.75, ease: 'easeIn' },
}}
variants={{
[HIDE]: {
fill: 'rgba(255, 255, 255, 0)',
opacity: 0,
pathLength: 0,
},
[SHOW]: {
fill: 'rgba(255, 255, 255, 1)',
opacity: 1,
pathLength: 1,
},
}}
/>
</m.svg>
<m.div
className="flex flex-col items-center"
variants={{
[HIDE]: {},
[SHOW]: { transition: { delayChildren: stagger(0.25) } },
}}
>
<h1 className="sr-only">{'Joshua L Geschwendt'}</h1>
<m.p
className="mb-8 max-w-xl text-center text-white"
variants={{
[HIDE]: { opacity: 0, y: 24 },
[SHOW]: {
opacity: 1,
transition: {
opacity: { duration: 0.5 },
y: { bounce: 0.5, duration: 1.5, type: 'spring' },
},
y: 0,
},
}}
>
<Statement />
</m.p>
<m.ul
className="flex text-white"
variants={{
[HIDE]: { opacity: 0 },
[SHOW]: {
opacity: 1,
transition: { delayChildren: stagger(0.1) },
},
}}
>
{links.map(([icon, label, href]) => (
<m.li
key={label}
variants={{
[HIDE]: { opacity: 0, y: 24 },
[SHOW]: {
opacity: 1,
transition: {
opacity: { duration: 0.5 },
y: { bounce: 0.5, duration: 1.25, type: 'spring' },
},
y: 0,
},
}}
>
<Link
aria-label={label}
className="mx-1 flex h-11 w-11 items-center justify-center rounded-lg border-2 border-[oklch(1_0_0/.05)] bg-[oklch(1_0_0/.05)] text-xl text-white transition duration-300 hover:border-[oklch(1_0_0/.1)] hover:bg-[oklch(1_0_0/.1)]"
href={href}
>
<icon.svg />
</Link>
</m.li>
))}
</m.ul>
</m.div>
</m.div>
</main>
);
}
|