Hinweis: Dieser Inhalt wurde automatisch übersetzt. Feedback senden

Persönliche Portfolio-Website

Ein modernes, content-fokussiertes Portfolio, gebaut mit Next.js 16, React 19 und TypeScript. Von Grund auf mit KI-gestützter Entwicklung erstellt.

Role: Developer & DesignerDez. 2025

Jahrelang habe ich mir gesagt, ich würde eine persönliche Website bauen. Die Domain verstaubte. Die Idee verließ nie die Notizen-App.

Dann habe ich aufgehört zu planen und angefangen zu bauen. Zwei Tage später war diese Website live.

Was sich geändert hat

Früher dachte ich, eine Website zu veröffentlichen bedeutet wochenlange Arbeit. Schriften auswählen. Layouts debuggen. Texte schreiben, die sich nie richtig anfühlten. Diesmal hatte ich einen anderen Ansatz: mit KI bauen, schnell veröffentlichen, später verbessern.

Das Ergebnis: 51 Commits in 48 Stunden. Ein Blog mit Audio-Narration. Ein Kontaktformular, das tatsächlich funktioniert. Ein Design-System, das ich erweitern kann. Nicht perfekt, aber real.

Was mich überraschte, war nicht die Geschwindigkeit. Es war, wie sich die Arbeit anfühlte. Weniger Suchen, mehr Bauen. Weniger Zweifeln, mehr Veröffentlichen. Die Tools erledigten die mühsamen Teile, sodass ich mich auf das Wesentliche konzentrieren konnte.

Diese Seite ist die vollständige Geschichte. Wie alles zusammenkam, was unter der Haube steckt, und was ich beim nächsten Mal anders machen würde.

Cursor IDE
Cursor IDE mit Claude-Assistent

Tech Stack

KategorieTechnologieVersionZweck
FrameworkNext.js (App Router)16.0.10Server-Komponenten, Routing, SSR
UI-BibliothekReact19.2.3Komponenten-Architektur
SpracheTypeScript5.xType Safety
StylingTailwind CSS4.xUtility-first Styling
Komponentenshadcn/uiNew YorkVorgefertigte zugängliche Komponenten
Contentnext-mdx-remote5.0.0MDX-Rendering mit Frontmatter
Parsinggray-matter4.0.3Frontmatter-Extraktion
IconsLucide React0.561.0SVG-Icon-Bibliothek
E-MailSendGrid8.1.6Kontaktformular-Zustellung
AnalyticsGoogle Analytics 4Traffic- und Verhaltens-Tracking
AudioElevenLabs Audio NativeText-to-Speech für Blog-Posts
Dependencies
Package.json-Abhängigkeiten

Architektur

Die Anwendung folgt den Next.js App Router-Konventionen mit klarer Trennung zwischen Seiten, Komponenten, Content und Utilities.

ftchvs_26/
├── app/                    # Next.js App Router Seiten
│   ├── layout.tsx          # Root-Layout mit Schriften und Nav
│   ├── page.tsx            # Startseite
│   ├── globals.css         # Globale Styles und Design-Token
│   ├── about/              # Über-mich-Seite
│   ├── blog/               # Blog-Liste und [slug]-Seiten
│   ├── projects/           # Projekt-Liste und [slug]-Seiten
│   ├── contact/            # Kontaktformular mit API-Route
│   └── resume/             # Lebenslauf-Seite
├── components/             # React-Komponenten
│   ├── nav.tsx             # Navigation mit mobilem Sheet
│   ├── theme-toggle.tsx    # Dark/Light-Mode-Toggle
│   ├── tts-player.tsx      # ElevenLabs Audio-Player
│   ├── mdx-content.tsx     # MDX-Renderer
│   └── ui/                 # shadcn/ui-Komponenten
├── content/                # MDX-Content-Dateien
│   ├── blog/               # Blog-Posts
│   ├── pages/              # Statische Seiten (about)
│   └── projects/           # Projekt-Fallstudien
├── lib/                    # Utilities
│   ├── content.ts          # Content-Laden und Parsen
│   ├── constants.ts        # Website-Konfiguration
│   ├── schema.tsx          # JSON-LD strukturierte Daten
│   └── utils.ts            # Hilfsfunktionen
└── public/                 # Statische Assets

Datenfluss

Content fließt von MDX-Dateien durch lib/content.ts zu den Seiten:

  1. MDX-Dateien speichern Content mit YAML-Frontmatter
  2. gray-matter parst Frontmatter in typisierte Objekte
  3. next-mdx-remote serialisiert MDX für React-Rendering
  4. Seiten-Komponenten laden Content zur Request-Zeit
  5. MDX-Komponenten rendern das finale HTML
GitHub Repo
GitHub-Repository-Struktur

Typografie-Design-System

Ich habe ein token-basiertes Typografie-System für konsistentes Styling auf allen Seiten gebaut. Jedes Token entspricht spezifischen Tailwind-Klassen.

TokenCSS-KlasseStylesVerwendung
Seitentitel.text-page-titletext-xl font-medium mb-6H1-Überschriften
Abschnittstitel.text-section-titletext-lg font-medium mt-8 mb-4H2-Überschriften
Unterabschnitt.text-subsection-titletext-base font-medium mt-6 mb-3H3-Überschriften
Fließtext.text-bodytext-[15px] leading-relaxed text-foreground/90Absätze
Fließtext-Container.text-body-containerspace-y-4 text-[15px] leading-relaxedMehrere Absätze
Meta.text-metatext-[14px] text-muted-foregroundDatum, Tags, Labels
Link.text-linkunderline underline-offset-2 hover:text-foregroundInline-Links
Listenelement.text-list-itemtext-sm text-foreground/80Karten- und Listentext

Schriftfamilie

  • Geist Sans — Primäre Schrift für Fließtext und UI
  • Geist Mono — Code-Blöcke und das Terminal-Logo

Beide Schriften werden über next/font/google mit display: swap für optimale Performance geladen.

Farbsystem

Farben verwenden den OKLCH-Farbraum für wahrnehmungsgleichmäßige Anpassungen. Das System umfasst:

  • Light Mode — Neutrale Graustufen mit subtilen Akzenten
  • Dark Mode — Dunkle Hintergründe mit hohem Kontrast-Text
  • Semantische Farben — Primary, Secondary, Muted, Accent, Destructive, Success

Hauptfunktionen

Dark Mode

Theme-Präferenz wird in localStorage gespeichert und respektiert Systemeinstellungen beim ersten Besuch. Ein blockierendes Skript im <head> verhindert den Flash des falschen Themes.

<script dangerouslySetInnerHTML={{
  __html: `(function(){
    var t = localStorage.getItem('theme') ||
      (matchMedia('(prefers-color-scheme:dark)').matches ? 'dark' : 'light');
    if (t === 'dark') document.documentElement.classList.add('dark')
  })();`,
}} />

Text-to-Speech

Der Player lädt dynamisch, um Hydration-Probleme zu vermeiden:

const TTSPlayer = dynamic(
  () => import('@/components/tts-player').then((mod) => mod.TTSPlayer),
  { ssr: false }
);

Einen Voice Clone auf ElevenLabs erstellen

Um deinen eigenen Voice Clone für den Audio-Player zu erstellen:

  1. Melde dich bei ElevenLabs an oder logge dich ein
  2. Navigiere zum Voices-Bereich im Dashboard (linke Seitenleiste)
  3. Klicke auf Add a new voice
  4. Wähle Professional Voice Clone (empfohlen für beste Qualität) oder Instant Voice Clone (schneller, erfordert weniger Audio)
  5. Lade Audio-Samples hoch:
    • Professional Voice Clone: Mindestens 1 Stunde hochwertige Aufnahmen (ideal 2-3 Stunden für beste Ergebnisse)
    • Instant Voice Clone: Minimum 30 Sekunden saubere Aufnahme
    • Teile lange Aufnahmen in ~30-Minuten-Samples für einfacheres Hochladen auf
    • Verwende saubere, hochwertige Aufnahmen ohne Hintergrundgeräusche, Echo oder unerwünschte Sounds
  6. Optional direkt im Interface aufnehmen mit Record yourself und bereitgestellten Skript-Vorlagen
  7. Benenne und labele deinen Voice Clone
  8. Bestätige, dass du das Recht und die Zustimmung hast, die Stimme zu klonen
  9. Klicke auf Save voice, um das Modell zu trainieren
  10. Nach dem Training kopiere die Voice ID aus den Stimmen-Einstellungen
  11. Konfiguriere sie im Projekt durch Aktualisierung von ELEVENLABS_VOICE_ID in lib/constants.ts

Voice Sample

Hier ist ein Sample der geklonten Stimme:

Stimmen-Sample
0:00 / 0:00

Um das Audio-Sample zu generieren, kannst du den ElevenLabs Text to Speech Playground verwenden. Wähle deine geklonte Stimme, gib den Text ein, klicke auf Generate, dann lade herunter und speichere die Datei als voice-sample.mp3 im public/audio/-Verzeichnis.

Blog-Audio-Narration (lokales Kokoro)

Update — April 2026. Blog-Beiträge und die About-Seite nutzen jetzt Kokoro-82M, ein TTS-Modell unter Apache-2.0-Lizenz (82M Parameter, Stimme af_heart), das lokal auf der CPU per kokoro-onnx läuft. Die Generierung ist deterministisch, kostenlos und unabhängig von jeder externen API. Das Stimm-Sample oben bleibt die ursprünglich mit ElevenLabs geklonte Stimme — nur die Blog-Narration wurde migriert.

Das Setup ist einmalig: npm run setup:kokoro erstellt ein gitignoriertes .kokoro/-Verzeichnis mit einem Python-venv und dem ONNX-Modell (~340 MB). Danach narriert npm run generate-audio den gesamten englischen Inhalt und aktualisiert public/audio/manifest.json mit Post-Hashes. Auf einem Apple M4 Max läuft das Modell mit etwa 5× Echtzeit — ein drei Minuten langer Beitrag braucht ungefähr dreißig Sekunden.

Kontaktformular

Das Kontaktformular verwendet SendGrid für E-Mail-Zustellung mit:

  • Client-seitiger Validierung
  • Server-seitiger API-Route unter /contact/api
  • Reply-to-Header für direkte Antworten
  • Fehlerbehandlung mit benutzerfreundlichen Nachrichten

SEO

Jede Seite enthält:

  • Metadata — Titel, Beschreibung, Keywords
  • Open Graph — Social-Sharing-Bilder und Text
  • Twitter Cards — Twitter-spezifische Formatierung
  • JSON-LD — Strukturierte Daten für Person, Website, Article, Breadcrumb
  • Sitemap — Automatisch generiert unter /sitemap.xml
  • Robots — Suchmaschinen-Crawl-Regeln
Vercel Dashboard
Vercel-Dashboard

Build-Prozess

Phase 1: Projekt-Setup

Gestartet mit create-next-app unter Verwendung des App Router-Templates. TypeScript, Tailwind CSS 4 hinzugefügt und shadcn/ui mit der New York-Style-Variante konfiguriert.

npx create-next-app@latest ftchvs_26 --typescript --tailwind --app
npx shadcn@latest init

Phase 2: Core-Layout

Das Root-Layout mit Geist-Schriften und einer responsiven Navigation gebaut. Die Nav enthält ein Desktop-Menü und ein mobiles Sheet (Slide-out-Drawer) von shadcn/ui.

Das Terminal-Logo (>_) verwendet eine Shimmer-Animation, die sich an Light- und Dark-Mode anpasst.

Phase 3: Content-System

lib/content.ts erstellt, um MDX-Laden zu handhaben:

  • getAllPosts() — Alle Blog-Posts sortiert nach Datum abrufen
  • getPostBySlug() — Einzelnen Post mit serialisiertem MDX abrufen
  • getAllProjects() — Alle Projekte abrufen
  • getProjectBySlug() — Einzelnes Projekt mit serialisiertem MDX abrufen
  • getPageBySlug() — Statische Seiten wie About abrufen

Jede Funktion liest aus dem Dateisystem, parst Frontmatter mit gray-matter und serialisiert Content mit next-mdx-remote.

Phase 4: Seiten

Sechs Haupt-Routen gebaut:

  • Home — Einführung mit Liste aktueller Beiträge
  • About — Beruflicher Hintergrund aus MDX geladen
  • Blog — Post-Liste und einzelne Post-Seiten
  • Projects — Projekt-Showcase (diese Seite)
  • Resume — Berufserfahrung und Skills
  • Contact — Formular mit SendGrid-Integration

Phase 5: Integrationen

SendGrid: API-Route konfiguriert, um E-Mails vom Kontaktformular zu senden. Der Absender muss in SendGrids Authentifizierungs-Einstellungen verifiziert sein.

Google Analytics 4: Über @next/third-parties hinzugefügt mit bedingtem Laden basierend auf NEXT_PUBLIC_GA_ID.

Phase 6: Design-System

Typografie-Design-Token in globals.css implementiert. OKLCH-Farbvariablen für Light- und Dark-Mode hinzugefügt. Shimmer-Animation mit CSS Custom Properties erstellt.

Phase 7: SEO

Metadata zu jeder Seite hinzugefügt. Sitemap- und robots.txt-Generatoren erstellt. JSON-LD strukturierte Daten für Person-, WebSite-, Article- und BreadcrumbList-Schemas implementiert.

Phase 8: Deployment

Zu GitHub gepusht und in Vercel importiert. Umgebungsvariablen konfiguriert:

  • NEXT_PUBLIC_SITE_URL — Kanonische URL
  • NEXT_PUBLIC_GA_ID — Google Analytics
  • SENDGRID_API_KEY — E-Mail-Zustellung
  • CONTACT_EMAIL_TO — Empfänger-Adresse
  • CONTACT_EMAIL_FROM — Verifizierter Absender
Vercel Deployment
Vercel-Deployment-Details

Changelog

Eine detaillierte Timeline der Entwicklung, extrahiert aus der Git-Commit-Historie. Neueste zuerst.

Entwicklungs-Statistiken

  • Gesamte Commits: 51
  • Entwicklungszeit: ~2 Tage
  • Hauptfunktionen: 8
  • Bugfixes: 12
  • Refactors: 15
  • Style-Updates: 16

13. Dezember 2025 — Tag 2: Polish & Features

Nacht: Finaler Polish

CommitBeschreibung
20107f4Major: Umfassende SEO-Verbesserungen
e3ec3c2About-Seiten-Intro vereinfacht
c21eb7bVoice-ID-Konfiguration zentralisiert
9311bc6Custom ElevenLabs-Stimme konfiguriert
f8533bfMajor: ElevenLabs optimiert, um API-Kosten zu reduzieren
f8d31cfTTS-Player über Summary positioniert
7da002aBlog-Listen-Typografie aktualisiert
872a6baBlog-Post für Klarheit und Storytelling überarbeitet

Abend: Audio-Optimierung

CommitBeschreibung
fc9cea4Ref verwendet, um HTML einzufügen (Hydration-Fix)
fb9303fiframe-Embed für Audio Native verwendet
7384478Suspense-Wrapper für MDX-Kompatibilität wiederhergestellt
ff9ca71Major: Audio Native API mit vollständigen Content-Projekten
76e6e08Suspense für SSR'd Audio Native entfernt
36c512cAudio Native via useEffect laden
5073332Public User ID hardcoded
ea4e5c7next/script für Audio Native-Laden verwendet
8a5ee20Zu Audio Native-Integration vereinfacht
32fa6caSkript zum Leeren des Audio-Cache hinzugefügt
61382caAudio-Caching hinzugefügt, um API-Aufrufe zu reduzieren
0ab2855TTS-Audio bei Seitenladen vorab generieren
d59988fDebug-Logging zu TTS-Player hinzugefügt

Später Nachmittag: Audio-Integrations-Sprint

CommitBeschreibung
d018e8eElevenLabs-Footer vom Player entfernt
156eda5Geschwindigkeits-Auswahl minimalistischer gestaltet
b87be4fElevenLabs-Header vom Player entfernt
1d6af2fVollständige Audio-Player-UI sofort anzeigen
1a90ebfTTS-Player mit ElevenLabs-UI-Komponenten aufgewertet
738ebb4Audio Native durch TTS API ersetzt
fa6fd42Cursor IDE-Konfiguration und Regeln hinzugefügt
5152aecMDXRemote mit Suspense für React 19 gewrapped
c7b54b2Major: ElevenLabs Audio Native TTS-Integration hinzugefügt

Nachmittag: Typografie & Lebenslauf

CommitBeschreibung
2ff75d4Lern-Statement im Blog-Post aktualisiert
0c9b17bLebenslauf-Tagline aktualisiert
3e6b185Strich-Präfix zu Skills und Ausbildungs-Items hinzugefügt
3371a03"Looking For"-Sektion aus Lebenslauf entfernt
649010dLebenslauf-Typografie und Ausrichtung verbessert
f03743eSchriftgrößen für Listenelemente, Links, Meta-Text aktualisiert

Später Vormittag: Content-Strategie

CommitBeschreibung
c5d35e4Zielgruppen-Targeting zur About-Seite hinzugefügt
45e61a2"Tools selbst bauen"-Messaging abgeschwächt
b7a8cd8README-Updates für neue Positionierung
ae9f3a3Growth-Marketer-Positionierung auf allen Seiten verfeinert
701a36fWebsite als "Growth Marketer, der programmiert" neu positioniert
db41419Projekte-Seite durch "Coming Soon"-Platzhalter ersetzt

Vormittag: Design-System

CommitBeschreibung
4b7f5ffREADME mit Typografie-Token, SendGrid, GA4-Dokumentation aktualisiert
d850b7eNav-Menü-Reihenfolge: About, Blog, Projects, Resume, Contact
a17536bMajor: Typografie-Design-Token-System implementiert
a91398fFavicon auf Terminal-Prompt-Icon (>_) aktualisiert
c85a8c8Focus-Ring-Border auf Terminal-Logo entfernt
af50e11Shimmer-Animation für Light/Dark-Mode-Support refaktoriert

12. Dezember 2025 — Tag 1: Foundation

CommitBeschreibung
0b4e0a2Major: SendGrid für Kontaktformular mit erweiterter UI integriert
5bf8d6aTextauswahl auf Terminal-Logo-Klick gefixt
2ab20b1Statische Generierung für MDX deaktiviert (React 19-Kompatibilitätsfix)
0d1a813Seiten, Komponenten und Core-Features hinzugefügt
891648cREADME-Dokumentation gemerged
08bc5e0Major: Initiale Next.js-Portfolio-Site mit Blog- und Projekt-Struktur
7850991Repository-Initialisierung auf GitHub
20aed18Initial Commit von Create Next App

Wichtige Meilensteine:

  • Vollständige App-Router-Struktur mit 6 Routen
  • MDX-Content-System mit gray-matter-Parsing
  • shadcn/ui-Komponenten-Bibliothek-Integration
  • Kontaktformular mit E-Mail-Zustellung
  • Responsive Navigation mit mobilem Drawer
Commit History
GitHub-Commit-Historie

Screenshots

Homepage Light
Startseite Light Mode
Homepage Dark
Startseite Dark Mode
Mobile Nav
Mobile Navigation
Contact Form
Kontaktformular
Blog Post
Blog-Post mit Audio-Player

Was ich gelernt habe

KI-gestützte Entwicklung ist real. Cursor und Claude erledigten Boilerplate, fanden Fehler und schlugen Patterns vor, die ich allein nicht gefunden hätte. Der Engpass verschob sich vom Tippen zum Denken.

TypeScript zahlt sich früh aus. Type-Fehler fingen Bugs, bevor sie den Browser erreichten. Die initiale Setup-Zeit sparte Stunden an Debugging.

Design-Token skalieren. Typografie-Klassen einmal zu definieren bedeutete konsistentes Styling ohne ständige Entscheidungen. Änderungen propagieren automatisch.

MDX ist flexibel. Frontmatter-Schemas lassen Content das UI steuern. Ein neues Feld hinzuzufügen dauert Minuten, nicht Refactors.


Nächste Schritte

  • Mehr Projekte als Fallstudien hinzufügen
  • Blog-Post-Suche und Filterung implementieren
  • RSS-Feed für Blog-Abonnenten hinzufügen
  • ISR (Incremental Static Regeneration) für Content-Updates erkunden

Outcomes

  • Von Grund auf mit KI-gestützter Entwicklung gebaut (Cursor + Claude)
  • Eigenes Typografie-Design-Token-System implementiert
  • ElevenLabs Audio Native für Text-to-Speech integriert
  • SendGrid für Kontaktformular-Funktionalität konfiguriert
  • Auf Vercel deployed mit umfassendem SEO

Links