Independent project · Portfolio · 2026
Built withNext.js · TypeScript · GSAP
jonatan-greis.de: This website is my longest work sample.
Scannable in 30 seconds, and still proof of how I build.
Anyone opening a portfolio wants to know quickly: which role, which work, which person. For a design engineer, though, the website is evidence in itself. So I designed it from scratch and built it in Next.js, with its own motion, two languages and two themes. These are the decisions you don't see at first glance.
- My contribution
- Concept, design, motion and build. Built with AI assistance in Cursor and Claude Code.
- Stack
- Next.js 16, TypeScript, Tailwind CSS 4, GSAP, Lenis, WebGL
- Status
- Live on jonatan-greis.de, statically prerendered on Vercel
01
Readable first, impressive second.
The home page follows a fixed order: role, work, person. Motion may accompany that order but never hold it up. Everything that moves has a resting state for reduced motion.
One clock for scroll and animation
Smooth scrolling runs in the same ticker as the animations. Scroll-linked motion listens to the same clock, so the two never drift apart.
Two languages, no detour
German lives unprefixed at /, English at /en. A server-level rewrite keeps both variants statically prerendered.
Two themes, one token set
Colours, spacing and easing curves are tokens. Light and dark share the same rules; switching runs as a circular view transition.
02
Three places where the obvious approach wasn't enough.
Each one: what I observed, what I built in response, and what it costs.
01
Page changes with a hard deadline
- Observation
- Clicking back and forth quickly left the transition stuck. Waiting for the destination relied on frame polling alone, which fell behind when many changes followed each other.
- Decision
- The transition waits until the destination is ready and also gets a hard deadline. A superseded change aborts cleanly.
- Trade-off
- On slow connections a change waits briefly instead of showing a half-loaded page.
02
A shader that recovers
- Observation
- The WebGL gradient in the footer did not come back after page transitions with an SVG filter. A resize didn't help, and releasing the context on purpose broke the next start.
- Decision
- The canvas is remounted when needed, which gives it a fresh surface. The context is only released on exit. Without WebGL or with reduced motion, a calm gradient stays in place.
- Trade-off
- More lifecycle code for an effect that only lives in the footer.
03
The same HTML on server and client
- Observation
- After the first production deploy, React reported a hydration error on the home page. Prerendering ran internally under /de, the browser under /. The contact CTA was therefore missing from the shipped HTML.
- Decision
- A small hook returns the public path everywhere. All twelve places that read the path use it; nothing changes in the browser.
- Trade-off
- One more abstraction to know about before reading the path.
03
What else is in there.
Smaller systems that together make the site feel calm.
Playground
24 interactive UI snippets in a dependency-free engine of their own, sharing one animation clock.
Private and fast
Fonts are self-hosted, there are no cookies and no tracking, plus a strict Content Security Policy.
Rules as tests
Automated tests check which cases are public, whether both languages are complete and whether style rules hold.
04
AI writes along. The decisions are mine.
The site was built with AI assistance, in Cursor and Claude Code. I set the structure, design and motion, and I check every change in the browser and on my phone. What proves itself becomes a decision in architecture notes and a rule in tests, so it holds through the next rebuild.