All projects

Work · 2026

This portfolio

Successor to a 33,000-line PHP application — static, bilingual, and free of a database.

Live

At a glance

  • No backend, no database, no login — served entirely as static files
  • Content as Markdown behind a Zod schema that breaks the build, not the live site
  • Bilingual DE/EN, with German unprefixed at the root
  • Interactivity in vanilla TypeScript rather than a UI framework

Why rebuild

The previous version was a custom PHP MVC framework with eleven controllers, eight models and thirteen database tables. It handled user accounts with TOTP and passkeys, a ticketing system with an AI chatbot, four games with OpenAI opponents, and an EV charging calculator.

What it barely did was show projects. The portfolio was three of those pages, and the project data lived in a MySQL table — so it was not in git, not versioned, not reviewable. A portfolio whose content cannot be read anywhere has missed its own point.

The cut

Everything that did not serve showing work was removed: authentication, tickets, games, calculators, the database, the server code. What remains is a static site sitting on the same Apache host as before — just as a dist/ folder rather than a front controller.

Project content lives as Markdown in the repository. A Zod schema in content.config.ts validates every field at build time. A missing year, a malformed URL, a project that exists in only one language — each of those fails npm run build instead of surfacing on the live site.

Why no CMS

An admin area would have required a server, a session and password handling — precisely the attack surface whose removal was the point of the rebuild. A new project is instead one file per language and one commit. Rollback, diff and history come for free.

Frontend

Reactivity comes from a handful of vanilla TypeScript islands: filter and search on the project list, a command palette, the theme switch, a pointer-tracked spotlight on the cards. No UI framework, because the interactive surface is too small to justify one. Filtering degrades cleanly without JavaScript — you simply get the unfiltered list.

The palette continues the three themes the old site had: noir by default, light and sakura as alternatives. Every animation sits behind prefers-reduced-motion.