commit da801b4ce279d25369f9f9e5cdb71d16f1e38677 Author: Jerrit Fritzsche Date: Thu Jul 23 10:33:53 2026 +0200 feat: initial portfolio site (Vite + TS + GSAP + Lenis) Dark-mode Portfolio mit Scroll-Animationen, Custom Cursor, Skill-Bars und Docker-Pipeline zu Gitea Packages. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a01184e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +node_modules +dist +.git +.gitignore +*.log +.vite +.gitea diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml new file mode 100644 index 0000000..99d26f6 --- /dev/null +++ b/.gitea/workflows/docker.yml @@ -0,0 +1,32 @@ +name: docker +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + build-and-push: + runs-on: docker + container: + image: catthehacker/ubuntu:act-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: https://github.com/docker/setup-buildx-action@v3 + + - name: Log in to Gitea Container Registry + run: echo "${{ secrets.REGISTRY_TOKEN }}" | docker login git.jfritzsche.de -u jf --password-stdin + + - name: Build and push + uses: https://github.com/docker/build-push-action@v6 + with: + context: . + push: true + tags: | + git.jfritzsche.de/jf/jf-homepage:latest + git.jfritzsche.de/jf/jf-homepage:${{ github.sha }} + + - name: Logout + run: docker logout git.jfritzsche.de diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d25799b --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules +dist +.DS_Store +*.log +.vite diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f2ef272 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +# Multi-stage: build mit Vite, serven mit nginx +FROM node:20-alpine AS build +WORKDIR /app +COPY package.json ./ +RUN npm install +COPY . . +RUN npm run build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +# Gitea-Container-Registry-Host als Cache-bust, sonst Standard-port 80 +EXPOSE 80 +HEALTHCHECK --interval=30s --timeout=3s \ + CMD wget -qO- http://localhost/ >/dev/null 2>&1 || exit 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..d640bf2 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# jf-homepage + +Portfolio & About-Seite für **Jerrit (jf)** — gebaut mit Vite, TypeScript, GSAP/ScrollTrigger und Lenis. + +## Stack + +- **Vite** — Dev-Server & Build +- **TypeScript** — Typsicherheit +- **GSAP + ScrollTrigger** — Scroll-Animationen, Reveals, Skill-Bars +- **Lenis** — Smooth Scrolling +- Reines CSS (Custom Properties, kein Framework) + +## Features + +- Animierter Hero (Intro-Timeline, Gradient-Background) +- Reveal-on-Scroll für alle Sektionen +- Skill-Bars mit Scroll-Trigger +- Custom Cursor + Scroll-Progress-Bar +- Fixe Nav mit Blur-on-Scroll +- Responsive (Desktop / Tablet / Mobile) +- `prefers-reduced-motion` respektiert + +## Struktur + +``` +jf-homepage/ +├── index.html # Markup, Sektionen +├── src/ +│ ├── style.css # komplettes Styling +│ └── main.ts # Animationen, Lenis, Cursor, Nav +├── package.json +└── tsconfig.json +``` + +## Local dev + +```bash +npm install +npm run dev # http://localhost:5173 +``` + +## Build + +```bash +npm run build # tsc + vite build -> dist/ +npm run preview # dist/ lokal serven +``` + +## Deploy + +Statisches `dist/` — hostbar auf jedem Static-Host (z. B. Gitea Pages, Netlify, Cloudflare Pages, nginx). + +## Git / Gitea + +- Remote: `git@git.jfritzsche.de:jf/jf-homepage.git` +- Branches: `main` (prod), `dev` (laufend), `feature/*`, `fix/*` +- Commits: Conventional Commits (`feat:`, `fix:`, `docs:`, `chore:`) +- CI/CD: aktuell keine Pipeline (statisches Hosting). + +## ToDo / Ideen + +- [ ] Echte Projekt-Cases statt Platzhalter +- [ ] Dark/Light-Toggle +- [ ] Gitea-Actions-Deploy zu Gitea Pages (optional) diff --git a/index.html b/index.html new file mode 100644 index 0000000..6b2ef96 --- /dev/null +++ b/index.html @@ -0,0 +1,188 @@ + + + + + + Jerrit · jf — Portfolio + + + + + + + + + + + + + + + + +
+ +
+ +
+

Entwickler · Maker · IT

+

+ Jerrit + Fritzsche +

+

+ 25 · Fortgeschrittener Entwickler seit ~15 Jahren. + Ich baue Web-Apps, Spiele, Tools & Game-Systeme — + und plane meine nebenberufliche Selbstständigkeit. +

+ +
+ + scroll + +
+ + +
+
+ 01 +

Über mich

+
+
+
+

+ Ich programmiere hobbymäßig, seit ich den Computer nicht mehr nur + zum Spielen nutze — mittlerweile etwa 15 Jahre. Viele Sprachen + verstehe ich rezeptiv sicher; wenn es drauf ankommt, schreibe ich + sauberen Code in dem, was das Projekt verlangt. +

+

+ Tagsüber leitender IT-Administrator im öffentlichen Dienst. + Abends und am Wochenende: eigene Projekte, Skills sharpen, und der + Weg in die nebenberufliche Selbstständigkeit. +

+
+
    +
  • Alter25
  • +
  • Erfahrung~15 Jahre
  • +
  • FokusWeb · Games · Tools
  • +
  • SetupmacOS · Devin · Orca
  • +
  • GitGitea · jf
  • +
+
+
+ + +
+
+ 02 +

Skills

+
+
+
+

Java

+
+
+
+

JavaScript / TS

+
+
+
+

Lua

+
+
+
+

HTML / CSS

+
+
+
+

Game-Systeme

+
+
+
+

DevOps / IT

+
+
+
+
+ + +
+
+ 03 +

Projekte

+
+ +
+ + +
+
+ 04 +

Kontakt

+
+
+

+ Lust auf ein Projekt, eine Idee oder einfach ein Gespräch über + Kaffee und Code? +

+ +
+
+
+ + + + + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2373e07 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1043 @@ +{ + "name": "jf-homepage", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "jf-homepage", + "version": "0.1.0", + "dependencies": { + "gsap": "^3.12.5", + "lenis": "^1.1.13" + }, + "devDependencies": { + "typescript": "^5.4.5", + "vite": "^5.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gsap": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", + "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, + "node_modules/lenis": { + "version": "1.3.25", + "resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.25.tgz", + "integrity": "sha512-mOKxayErlaONK8fm4LN3XNd99Qu4plTpn9h9qf8wxzjGrJDzuD84FYzZ81HCd6ZsWp++VWVwOzL286Pf2s2u4A==", + "license": "MIT", + "workspaces": [ + "packages/*", + "playground", + "playground/*" + ], + "funding": { + "type": "github", + "url": "https://github.com/sponsors/darkroomengineering" + }, + "peerDependencies": { + "@nuxt/kit": ">=3.0.0", + "react": ">=17.0.0", + "vue": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@nuxt/kit": { + "optional": true + }, + "react": { + "optional": true + }, + "vue": { + "optional": true + } + } + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz", + "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ed52e3b --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "jf-homepage", + "private": true, + "version": "0.1.0", + "type": "module", + "description": "Portfolio & About — Jerrit (jf)", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview" + }, + "dependencies": { + "gsap": "^3.12.5", + "lenis": "^1.1.13" + }, + "devDependencies": { + "typescript": "^5.4.5", + "vite": "^5.4.0" + } +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..11bb0b4 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,121 @@ +import "./style.css"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import Lenis from "lenis"; + +gsap.registerPlugin(ScrollTrigger); + +/* ---------- Smooth scroll (Lenis) ---------- */ +const lenis = new Lenis({ + duration: 1.1, + easing: (t: number) => Math.min(1, 1.001 - Math.pow(2, -10 * t)), +}); +lenis.on("scroll", ScrollTrigger.update); +gsap.ticker.add((time: number) => lenis.raf(time * 1000)); +gsap.ticker.lagSmoothing(0); + +/* Anchor-Links über Lenis */ +document.querySelectorAll('a[href^="#"]').forEach((a) => { + a.addEventListener("click", (e) => { + const id = a.getAttribute("href"); + if (!id || id === "#") return; + const target = document.querySelector(id); + if (target) { + e.preventDefault(); + lenis.scrollTo(target as HTMLElement, { offset: -80 }); + } + }); +}); + +/* ---------- Custom cursor ---------- */ +const cursor = document.querySelector(".cursor"); +const dot = document.querySelector(".cursor-dot"); +if (cursor && dot && window.matchMedia("(hover: hover)").matches) { + let mx = 0, + my = 0, + cx = 0, + cy = 0; + window.addEventListener("mousemove", (e) => { + mx = e.clientX; + my = e.clientY; + dot.style.transform = `translate(${mx}px, ${my}px) translate(-50%, -50%)`; + }); + gsap.ticker.add(() => { + cx += (mx - cx) * 0.18; + cy += (my - cy) * 0.18; + cursor.style.transform = `translate(${cx}px, ${cy}px) translate(-50%, -50%)`; + }); + const hoverables = "a, button, .card"; + document.querySelectorAll(hoverables).forEach((el) => { + el.addEventListener("mouseenter", () => cursor.classList.add("is-hover")); + el.addEventListener("mouseleave", () => cursor.classList.remove("is-hover")); + }); +} + +/* ---------- Scroll progress + nav state ---------- */ +const progress = document.querySelector(".scroll-progress"); +const nav = document.querySelector("#nav"); +lenis.on("scroll", ({ scroll, limit }: { scroll: number; limit: number }) => { + const p = limit > 0 ? scroll / limit : 0; + if (progress) progress.style.width = `${p * 100}%`; + if (nav) nav.classList.toggle("is-scrolled", scroll > 40); +}); + +/* ---------- Hero intro ---------- */ +const heroTl = gsap.timeline({ defaults: { ease: "power3.out" } }); +heroTl + .from(".hero__eyebrow", { y: 20, opacity: 0, duration: 0.6 }) + .from( + ".hero__title .line", + { yPercent: 120, opacity: 0, duration: 0.9, stagger: 0.12 }, + "-=0.2" + ) + .from(".hero__sub", { y: 24, opacity: 0, duration: 0.7 }, "-=0.5") + .from(".hero__cta .btn", { y: 20, opacity: 0, duration: 0.5, stagger: 0.1 }, "-=0.4") + .from(".hero__scroll", { opacity: 0, duration: 0.6 }, "-=0.2"); + +/* ---------- Reveal on scroll ---------- */ +gsap.utils.toArray("[data-reveal]").forEach((el) => { + gsap.from(el, { + y: 40, + opacity: 0, + duration: 0.8, + ease: "power3.out", + scrollTrigger: { trigger: el, start: "top 85%" }, + }); +}); + +/* ---------- Skill bars ---------- */ +gsap.utils.toArray(".skill__bar i").forEach((bar) => { + const level = Number(bar.dataset.level || 0); + gsap.to(bar, { + width: `${level}%`, + duration: 1.2, + ease: "power3.out", + scrollTrigger: { trigger: bar, start: "top 90%" }, + }); +}); + +/* ---------- Footer year ---------- */ +const year = document.getElementById("year"); +if (year) year.textContent = String(new Date().getFullYear()); + +/* ---------- Mobile nav toggle (simple) ---------- */ +const toggle = document.getElementById("navToggle"); +if (toggle) { + toggle.addEventListener("click", () => { + const links = document.querySelector(".nav__links"); + if (links) { + const open = links.style.display === "flex"; + links.style.display = open ? "" : "flex"; + links.style.position = "absolute"; + links.style.top = "100%"; + links.style.right = "20px"; + links.style.flexDirection = "column"; + links.style.background = "rgba(10,10,15,0.95)"; + links.style.padding = "20px"; + links.style.borderRadius = "12px"; + links.style.border = "1px solid var(--line)"; + } + }); +} diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..dc75a39 --- /dev/null +++ b/src/style.css @@ -0,0 +1,499 @@ +:root { + --bg: #0a0a0f; + --bg-soft: #111119; + --fg: #f2f2f5; + --muted: #9a9aac; + --accent: #6c5ce7; + --accent-2: #00d2ff; + --line: rgba(255, 255, 255, 0.08); + --radius: 14px; + --maxw: 1180px; + --ease: cubic-bezier(0.22, 1, 0.36, 1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: auto; /* Lenis übernimmt */ +} + +body { + background: var(--bg); + color: var(--fg); + font-family: "Inter", system-ui, sans-serif; + line-height: 1.6; + overflow-x: hidden; + -webkit-font-smoothing: antialiased; +} + +a { + color: inherit; + text-decoration: none; +} + +h1, +h2, +h3 { + font-family: "Space Grotesk", system-ui, sans-serif; + font-weight: 700; + line-height: 1.05; + letter-spacing: -0.02em; +} + +/* ---------- Cursor ---------- */ +.cursor, +.cursor-dot { + position: fixed; + top: 0; + left: 0; + pointer-events: none; + z-index: 9999; + border-radius: 50%; + mix-blend-mode: difference; +} +.cursor { + width: 38px; + height: 38px; + border: 1.5px solid #fff; + transform: translate(-50%, -50%); + transition: width 0.25s var(--ease), height 0.25s var(--ease), + background 0.25s var(--ease); +} +.cursor-dot { + width: 6px; + height: 6px; + background: #fff; + transform: translate(-50%, -50%); +} +.cursor.is-hover { + width: 64px; + height: 64px; + background: rgba(255, 255, 255, 0.12); +} +@media (hover: none) { + .cursor, + .cursor-dot { + display: none; + } +} + +/* ---------- Scroll progress ---------- */ +.scroll-progress { + position: fixed; + top: 0; + left: 0; + height: 3px; + width: 0%; + background: linear-gradient(90deg, var(--accent), var(--accent-2)); + z-index: 9998; +} + +/* ---------- Nav ---------- */ +.nav { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + display: flex; + align-items: center; + justify-content: space-between; + padding: 22px clamp(20px, 5vw, 60px); + backdrop-filter: blur(10px); + background: rgba(10, 10, 15, 0.4); + border-bottom: 1px solid transparent; + transition: background 0.3s var(--ease), border-color 0.3s var(--ease); +} +.nav.is-scrolled { + background: rgba(10, 10, 15, 0.75); + border-bottom: 1px solid var(--line); +} +.nav__logo { + font-family: "Space Grotesk", sans-serif; + font-weight: 700; + font-size: 1.4rem; +} +.nav__logo span { + color: var(--accent); +} +.nav__links { + display: flex; + gap: 30px; + font-size: 0.95rem; +} +.nav__links a { + color: var(--muted); + position: relative; + transition: color 0.25s var(--ease); +} +.nav__links a::after { + content: ""; + position: absolute; + left: 0; + bottom: -4px; + width: 0; + height: 1.5px; + background: var(--accent); + transition: width 0.25s var(--ease); +} +.nav__links a:hover { + color: var(--fg); +} +.nav__links a:hover::after { + width: 100%; +} +.nav__toggle { + display: none; + background: none; + border: none; + color: var(--fg); + font-size: 1.4rem; + cursor: pointer; +} + +/* ---------- Hero ---------- */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + padding: 0 clamp(20px, 5vw, 60px); + overflow: hidden; +} +.hero__bg { + position: absolute; + inset: 0; + background: radial-gradient( + 60% 60% at 70% 20%, + rgba(108, 92, 231, 0.25), + transparent 60% + ), + radial-gradient(50% 50% at 20% 80%, rgba(0, 210, 255, 0.18), transparent 60%); + filter: blur(20px); + animation: drift 18s ease-in-out infinite alternate; +} +@keyframes drift { + to { + transform: translate3d(40px, -30px, 0) scale(1.1); + } +} +.hero__content { + position: relative; + max-width: var(--maxw); + margin: 0 auto; + width: 100%; +} +.hero__eyebrow { + text-transform: uppercase; + letter-spacing: 0.3em; + font-size: 0.8rem; + color: var(--accent-2); + margin-bottom: 24px; +} +.hero__title { + font-size: clamp(3.5rem, 12vw, 9rem); + line-height: 0.95; +} +.hero__title .line { + display: block; + overflow: hidden; +} +.hero__title .line--accent { + background: linear-gradient(90deg, var(--accent), var(--accent-2)); + -webkit-background-clip: text; + background-clip: text; + color: transparent; +} +.hero__sub { + max-width: 620px; + margin-top: 30px; + color: var(--muted); + font-size: clamp(1rem, 1.6vw, 1.2rem); +} +.hero__cta { + display: flex; + gap: 16px; + margin-top: 40px; + flex-wrap: wrap; +} +.btn { + padding: 14px 28px; + border-radius: 40px; + font-weight: 500; + font-size: 0.95rem; + transition: transform 0.25s var(--ease), box-shadow 0.25s var(--ease), + background 0.25s var(--ease); +} +.btn--primary { + background: linear-gradient(90deg, var(--accent), var(--accent-2)); + color: #0a0a0f; +} +.btn--primary:hover { + transform: translateY(-3px); + box-shadow: 0 14px 40px rgba(108, 92, 231, 0.4); +} +.btn--ghost { + border: 1px solid var(--line); + color: var(--fg); +} +.btn--ghost:hover { + border-color: var(--accent); + transform: translateY(-3px); +} +.hero__scroll { + position: absolute; + bottom: 34px; + left: 50%; + transform: translateX(-50%); + display: flex; + flex-direction: column; + align-items: center; + gap: 10px; + font-size: 0.75rem; + letter-spacing: 0.2em; + text-transform: uppercase; + color: var(--muted); +} +.hero__scroll span { + width: 1px; + height: 46px; + background: linear-gradient(var(--accent), transparent); + animation: scrollPulse 2s var(--ease) infinite; +} +@keyframes scrollPulse { + 0% { + transform: scaleY(0); + transform-origin: top; + } + 50% { + transform: scaleY(1); + transform-origin: top; + } + 51% { + transform-origin: bottom; + } + 100% { + transform: scaleY(0); + transform-origin: bottom; + } +} + +/* ---------- Sections ---------- */ +.section { + max-width: var(--maxw); + margin: 0 auto; + padding: clamp(80px, 12vh, 160px) clamp(20px, 5vw, 60px); +} +.section__head { + display: flex; + align-items: baseline; + gap: 20px; + margin-bottom: 60px; +} +.section__index { + font-family: "Space Grotesk", sans-serif; + color: var(--accent); + font-size: 0.9rem; +} +.section__head h2 { + font-size: clamp(2rem, 5vw, 3.4rem); +} + +/* About */ +.about__grid { + display: grid; + grid-template-columns: 1.4fr 1fr; + gap: 50px; +} +.about__lead p { + font-size: 1.15rem; + color: var(--fg); + margin-bottom: 22px; +} +.about__lead p:last-child { + color: var(--muted); +} +.about__facts { + list-style: none; + display: grid; + gap: 14px; +} +.about__facts li { + display: flex; + justify-content: space-between; + padding: 14px 0; + border-bottom: 1px solid var(--line); + font-size: 0.95rem; +} +.about__facts strong { + color: var(--muted); + font-weight: 500; +} +.about__facts span { + font-family: "Space Grotesk", sans-serif; +} + +/* Skills */ +.skills__grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 26px; +} +.skill h3 { + font-size: 1.1rem; + margin-bottom: 12px; +} +.skill__bar { + height: 6px; + border-radius: 6px; + background: var(--bg-soft); + overflow: hidden; +} +.skill__bar i { + display: block; + height: 100%; + width: 0; + border-radius: 6px; + background: linear-gradient(90deg, var(--accent), var(--accent-2)); + box-shadow: 0 0 12px rgba(108, 92, 231, 0.35); +} + +/* Work */ +.work__grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 24px; +} +.card { + display: block; + padding: 34px; + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--bg-soft); + transition: transform 0.35s var(--ease), border-color 0.35s var(--ease), + background 0.35s var(--ease); + position: relative; + overflow: hidden; +} +.card::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient( + 120% 120% at 0% 0%, + rgba(108, 92, 231, 0.15), + transparent 50% + ); + opacity: 0; + transition: opacity 0.35s var(--ease); +} +.card:hover { + transform: translateY(-6px); + border-color: var(--accent); +} +.card:hover::before { + opacity: 1; +} +.card__tag { + font-size: 0.72rem; + text-transform: uppercase; + letter-spacing: 0.18em; + color: var(--accent-2); +} +.card h3 { + font-size: 1.5rem; + margin: 12px 0 10px; +} +.card p { + color: var(--muted); + font-size: 0.98rem; +} +.card__more { + display: inline-block; + margin-top: 18px; + color: var(--fg); + font-size: 0.9rem; +} + +/* Contact */ +.contact__lead { + font-size: clamp(1.4rem, 3.5vw, 2.4rem); + font-family: "Space Grotesk", sans-serif; + max-width: 720px; + line-height: 1.25; +} +.contact__links { + display: flex; + gap: 30px; + margin-top: 40px; + flex-wrap: wrap; +} +.contact__links a { + font-size: 1.1rem; + border-bottom: 1px solid var(--line); + padding-bottom: 4px; + transition: border-color 0.25s var(--ease), color 0.25s var(--ease); +} +.contact__links a:hover { + border-color: var(--accent); + color: var(--accent-2); +} + +/* Footer */ +.footer { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + gap: 10px; + padding: 30px clamp(20px, 5vw, 60px); + border-top: 1px solid var(--line); + color: var(--muted); + font-size: 0.85rem; + max-width: var(--maxw); + margin: 0 auto; + width: 100%; +} + +/* ---------- Reveal ---------- */ +[data-reveal], +[data-reveal-line] { + will-change: transform, opacity; +} + +/* ---------- Responsive ---------- */ +@media (max-width: 860px) { + .nav__links { + display: none; + } + .nav__toggle { + display: block; + } + .about__grid { + grid-template-columns: 1fr; + gap: 30px; + } + .skills__grid { + grid-template-columns: 1fr 1fr; + } + .work__grid { + grid-template-columns: 1fr; + } +} +@media (max-width: 520px) { + .skills__grid { + grid-template-columns: 1fr; + } +} + +/* Reduced motion */ +@media (prefers-reduced-motion: reduce) { + .hero__bg, + .hero__scroll span { + animation: none; + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c53327d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "moduleResolution": "bundler", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noEmit": true, + "skipLibCheck": true, + "isolatedModules": true, + "resolveJsonModule": true + }, + "include": ["src"] +}