Statt Symlink index.html → landingpage/index.html legen die Deploy-Skripte jetzt webroot/index.html (meta-refresh + location.replace) in den Webroot. README: Deploy-Schritte aktualisiert, Fehlerseiten-Konfig auf nginx (error_page) statt Apache korrigiert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.1 KiB
jamulix.de — Landing Page
Next.js 16 Landing Page für jamulix.de, statisch exportiert und unter /landingpage/ deployed.
Tech Stack
- Next.js 16.2 mit
output: 'export'(statischer Export, kein Node.js-Server nötig) - React 19, TypeScript
- Tailwind CSS v4 + shadcn/ui-Komponenten
- Framer Motion für Animationen
- next-themes für Dark/Light Mode
Projektstruktur
app/
layout.tsx # Root Layout, Fonts, Theme-Init-Script
page.tsx # Hauptseite (alle Sections zusammengesetzt)
globals.css # CSS-Variablen (OKLCH), Tailwind, Fly.io-Farbschema
impressum/ # Impressum-Seite
datenschutz/ # Datenschutzerklärung
components/
hero.tsx # Hero-Section mit Gradient-Überschrift
about-section.tsx
projects-section.tsx
focus-section.tsx
philosophy-section.tsx
footer.tsx # Footer mit E-Mail (obfuskiert) und Forgejo-Link
header.tsx # Navigation + Theme-Toggle
programmer-graphic.tsx # Foto (statischer Import wegen basePath)
theme-toggle.tsx
ui/ # shadcn/ui-Basiskomponenten
public/
Programmierer.png # Foto (statischer Import in programmer-graphic.tsx)
Lokale Entwicklung
npm install
npm run dev
Aufruf im Browser:
- Lokal: http://localhost:3000/landingpage
- Netzwerk: http://192.168.179.124:3000/landingpage (Handy im WLAN)
Port 3000 muss in der Firewall offen sein:
sudo ufw allow 3000/tcp
Build
npm run build
Erzeugt out/, benennt es in landingpage/ um. Dieses Verzeichnis wird deployed.
Persönliche Daten (Impressum / Datenschutz)
Werden nicht im Repository gespeichert, sondern zur Build-Zeit aus .env.local gelesen.
cp .env.local.example .env.local
# .env.local mit echten Daten befüllen
Variablen: OWNER_NAME, OWNER_STREET, OWNER_ZIP_CITY, OWNER_EMAIL,
HOSTER_NAME, HOSTER_STREET, HOSTER_ZIP, HOSTER_CITY, HOSTER_COUNTRY
.env.local ist per .gitignore ausgeschlossen.
Die E-Mail im Footer (footer.tsx) wird als Prop von app/page.tsx übergeben:
<Footer email={process.env.OWNER_EMAIL} />
Deployment
Production (jamulix.de)
./deploy_to_production.sh
- Baut die Seite (
npm run build) - Öffnet eine SSH-Master-Verbindung (Passwort nur einmal eingeben)
- rsync nach
jamulix.de:/var/www/jamulix.de/html/landingpage/ - Lädt
webroot/index.htmlnach/var/www/jamulix.de/html/index.htmlhoch (Weiterleitung vonjamulix.de/auf/landingpage/) - Kopiert die Fehlerseiten
landingpage/<code>.htmlin den Webroot
Localhost (zum Testen)
./deploy_to_localhost.sh
- rsync nach
/var/www/html/landingpage/ - Kopiert
webroot/index.html(Weiterleitung) nach/var/www/html/index.html - Kopiert die Fehlerseiten in den Webroot
sudo chown -R www-data:www-data
Warum /landingpage/ als Unterverzeichnis?
basePath: '/landingpage' in next.config.mjs erlaubt es, weitere Projekte
unter jamulix.de/ zu betreiben ohne Konflikte. rsync mit --delete löscht
nur Dateien innerhalb von landingpage/, nicht andere Verzeichnisse im Webroot.
Fehlerseiten
| Code | Quelle | Thema |
|---|---|---|
| 401 | app/401/page.tsx |
Autorisierung erforderlich |
| 403 | app/403/page.tsx |
Zugriff verweigert |
| 404 | app/not-found.tsx |
Seite nicht gefunden |
| 500 | app/500/page.tsx |
Interner Serverfehler |
| 502 | app/502/page.tsx |
Bad Gateway |
| 503 | app/503/page.tsx |
Wartungsarbeiten |
| 504 | app/504/page.tsx |
Gateway Timeout |
Gemeinsames Layout: components/error-page.tsx. Der Build erzeugt
landingpage/<code>.html; die Deploy-Skripte kopieren sie zusätzlich nach
<webroot>/<code>.html. Alle Asset-Pfade sind absolut (/landingpage/_next/…),
daher funktionieren die Kopien im Webroot genauso.
Damit nginx sie ausliefert, braucht der server-Block (nicht Teil
dieses Repos) z. B.:
error_page 401 /401.html;
error_page 403 /403.html;
error_page 404 /404.html;
error_page 500 /500.html;
error_page 502 /502.html;
error_page 503 /503.html;
error_page 504 /504.html;
- Ziel muss ein lokaler Pfad sein, keine vollständige URL (sonst wird daraus ein Redirect).
- Die 401-Seite erscheint erst, wenn der Login-Dialog des Browsers abgebrochen wird.
Design
Fly.io-inspiriertes Farbschema (Variant A+B+C):
- Dark Mode: Tiefes Navy (
oklch(0.10 0.02 260)) + Violet-Akzent (oklch(0.65 0.22 280)) - Light Mode: Warme Papiertöne + Violet-Akzent
- Font: Plus Jakarta Sans (Headlines + Body), JetBrains Mono (Code)
- Hero-Überschrift: Cyan→Violet Gradient im Dark Mode
- Gradient Mesh im Hero-Hintergrund (nur Dark Mode)
Dark/Light Mode wird per localStorage gespeichert. Das Init-Script in
app/layout.tsx verhindert Flash beim Laden.
Bilder
public/Programmierer.png muss als statischer Import eingebunden werden:
import programmiererImg from '../public/Programmierer.png'
// ...
<Image src={programmiererImg} ... />
Grund: next/image mit src="/Programmierer.png" (String) berücksichtigt
basePath nicht korrekt bei statischem Export.