slides, first slide
Some checks failed
Deploy Homepage / build (push) Failing after 1m4s

This commit is contained in:
2025-07-20 12:10:22 +02:00
parent 8c30acb8bb
commit cf96fbf6bd
10 changed files with 283 additions and 13 deletions

46
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "homepage", "name": "homepage",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"framer-motion": "^12.23.6",
"next": "15.4.2", "next": "15.4.2",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0" "react-dom": "19.1.0"
@@ -22,6 +23,9 @@
"eslint-config-next": "15.4.2", "eslint-config-next": "15.4.2",
"tailwindcss": "^4", "tailwindcss": "^4",
"typescript": "^5" "typescript": "^5"
},
"engines": {
"node": ">=20.0.0"
} }
}, },
"node_modules/@alloc/quick-lru": { "node_modules/@alloc/quick-lru": {
@@ -3326,6 +3330,33 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/framer-motion": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.6.tgz",
"integrity": "sha512-dsJ389QImVE3lQvM8Mnk99/j8tiZDM/7706PCqvkQ8sSCnpmWxsgX+g0lj7r5OBVL0U36pIecCTBoIWcM2RuKw==",
"license": "MIT",
"dependencies": {
"motion-dom": "^12.23.6",
"motion-utils": "^12.23.6",
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -4598,6 +4629,21 @@
"url": "https://github.com/sponsors/isaacs" "url": "https://github.com/sponsors/isaacs"
} }
}, },
"node_modules/motion-dom": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.6.tgz",
"integrity": "sha512-G2w6Nw7ZOVSzcQmsdLc0doMe64O/Sbuc2bVAbgMz6oP/6/pRStKRiVRV4bQfHp5AHYAKEGhEdVHTM+R3FDgi5w==",
"license": "MIT",
"dependencies": {
"motion-utils": "^12.23.6"
}
},
"node_modules/motion-utils": {
"version": "12.23.6",
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
"license": "MIT"
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",

View File

@@ -12,19 +12,20 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"framer-motion": "^12.23.6",
"next": "15.4.2",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0"
"next": "15.4.2"
}, },
"devDependencies": { "devDependencies": {
"typescript": "^5", "@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@tailwindcss/postcss": "^4",
"tailwindcss": "^4",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "15.4.2", "eslint-config-next": "15.4.2",
"@eslint/eslintrc": "^3" "tailwindcss": "^4",
"typescript": "^5"
} }
} }

View File

@@ -1,7 +1,14 @@
@import "tailwindcss"; @import "tailwindcss";
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base { @layer base {
body { body {
@apply bg-gray-900 text-gray-200 antialiased; @apply bg-gray-900 text-gray-200 antialiased;
} }
html, body {
@apply h-full overflow-hidden;
}
} }

View File

@@ -1,9 +1,62 @@
'use client'
import { useState, useEffect, useCallback } from 'react'
import Slide from '@/components/Slide';
import Slide1 from '@/components/Slide1';
import Slide2 from '@/components/Slide2';
import Slide3 from '@/components/Slide3';
import Slide4 from '@/components/Slide4';
import Slide5 from '@/components/Slide5';
const slides = [
<Slide1 key={1} />,
//<Slide2 key={2} />,
//<Slide3 key={3} />,
//<Slide4 key={4} />,
//<Slide5 key={5} />,
]
export default function Home() { export default function Home() {
const [current, setCurrent] = useState(0)
const nextSlide = useCallback(() => {
if (current < slides.length - 1) setCurrent(current + 1)
}, [current])
const prevSlide = useCallback(() => {
if (current > 0) setCurrent(current - 1)
}, [current])
useEffect(() => {
const handleScroll = (e: WheelEvent) => {
if (e.deltaY > 0) nextSlide()
else prevSlide()
}
const handleKey = (e: KeyboardEvent) => {
if (e.key === 'ArrowDown') nextSlide()
if (e.key === 'ArrowUp') prevSlide()
}
window.addEventListener('wheel', handleScroll)
window.addEventListener('keydown', handleKey)
return () => {
window.removeEventListener('wheel', handleScroll)
window.removeEventListener('keydown', handleKey)
}
}, [current, nextSlide, prevSlide])
return ( return (
<div className="flex min-h-screen flex-col items-center justify-between p-24"> <main className="relative h-screen w-screen overflow-hidden">
<main className="flex flex-col items-center justify-center"> {slides.map((slide, idx) => (
<h1 className="text-4xl font-bold">Work in Progress</h1> <Slide key={idx} index={idx} currentSlide={current}>
</main> {slide}
</div> </Slide>
); ))}
} </main>
)
}

25
src/components/Slide.tsx Normal file
View File

@@ -0,0 +1,25 @@
import { motion } from 'framer-motion';
import { ReactNode } from 'react';
type SlideProps = {
children: ReactNode
index: number
currentSlide: number
}
export default function Slide({ children, index, currentSlide }: SlideProps) {
const isActive = index === currentSlide
return (
<motion.div
className={`absolute top-0 left-0 w-full h-full flex flex-col items-center justify-center text-center gap-4 text-4xl`}
initial={{ opacity: 0 }} // Start off-screen
// x to left-100% for previous slides, 0 for current, and right-100% for next slides
animate={{ x: isActive ? 0 : index < currentSlide ? '-100%' : '100%', opacity: isActive ? 1 : 0 }}
transition={{ duration: 0.8 }}
>
{children}
</motion.div>
)
}

106
src/components/Slide1.tsx Normal file
View File

@@ -0,0 +1,106 @@
'use client'
import { motion, MotionValue, useTime, useTransform } from 'framer-motion';
import { useMemo } from 'react';
const titles = [
'Full Stack Developer',
'DevOps Engineer',
'Game Developer',
'Open Source Contributor',
'Tech Enthusiast',
'Gamer',
];
function Title({ title, progress, range, ys, os }: { title: string, progress: MotionValue<number>, range: number[], ys: string[], os: number[] }) {
const y = useTransform(progress, range, ys);
const opacity = useTransform(progress, range, os);
return (
<motion.div
className="absolute w-full text-center text-4xl font-bold"
style={{
y,
opacity
}}
>
{title}
</motion.div>
);
}
export default function Slide1() {
const time = useTime();
const genRanges = useMemo(() => {
const ranges = [];
for (let i = 0; i < titles.length+1; i++) {
const start = i * 1500 + 250 * i;
const end = start + 1500;
ranges.push(start, end);
}
return ranges;
}, []);
const genYs = useMemo(() => {
const ys = [];
for (let i = 0; i < titles.length; i++) {
ys.push([]);
for (let j = 0; j < titles.length+1; j++) {
if ((j % titles.length) === i) {
ys[i].push('0%', '0%'); // Current title
} else if (j < i) {
ys[i].push('100%', '100%'); // Previous titles
} else if (j == i + 1) {
ys[i].push('-100%', '100%'); // Next title
} else {
ys[i].push('100%', '100%'); // Titles after next
}
}
}
return ys;
}, []);
const genOs = useMemo(() => {
const os = [];
for (let i = 0; i < titles.length; i++) {
os.push([]);
for (let j = 0; j < titles.length+1; j++) {
if ((j % titles.length) === i) {
os[i].push(1, 1); // Current title
} else if (j < i) {
os[i].push(0, 0); // Previous titles
} else if (j == i + 1) {
os[i].push(0, 0); // Next title
} else {
os[i].push(0, 0); // Titles after next
}
}
}
return os;
}, []);
const progress = useTransform(time, (t) => ((t * 2) % genRanges[genRanges.length - 2]));
//
return (
<>
<p>I&apos;m Kay</p>
<div className="relative flex flex-col h-10 w-full">
{titles.map((title, i) => (
<Title
key={i}
title={title}
progress={progress}
range={genRanges}
ys={genYs[i]}
os={genOs[i]}
/>
))}
</div>
</>
)
}

View File

@@ -0,0 +1,8 @@
export default function Slide2() {
return (
<div>
</div>
)
}

View File

@@ -0,0 +1,8 @@
export default function Slide3() {
return (
<div>
</div>
)
}

View File

@@ -0,0 +1,8 @@
export default function Slide4() {
return (
<div>
</div>
)
}

View File

@@ -0,0 +1,8 @@
export default function Slide5() {
return (
<div>
</div>
)
}