Wie diese Website gebaut, getestet und veröffentlicht wird
3. Mai 2026
Diese Seite ist bewusst einfach gehalten: eine statisch generierte Website ohne Backend und ohne Client-seitiges JavaScript (abgesehen von den üblichen Drittanbieter-Skripten wie Google Tag Manager und Cookiebot). Hier ist ein kurzer Überblick, wie das Ganze aufgebaut ist.
Templating: 11ty mit Nunjucks
Die Seiten sind einfache HTML-Dateien, die ein gemeinsames Layout erweitern. 11ty (Eleventy) übernimmt das Rendering, Nunjucks liefert die Template-Syntax:
{% extends "layout.html" %}
{% block title %}Blog – Felix Boers{% endblock %}
{% block content %}
<section>...</section>
{% endblock %}
Das Layout definiert Blöcke für Titel, Meta-Tags, Open-Graph-Daten und den eigentlichen Inhalt, sodass jede Seite nur überschreibt, was sie wirklich braucht.
Styling: Tailwind v4 ganz ohne Config-Datei
Seit Version 4 konfiguriert man Tailwind direkt in CSS statt in einer
tailwind.config.js. Die Design-Tokens dieser Seite (Farben, Schriftart)
stehen in einem @theme-Block:
@theme {
--color-accent: #4a9edd;
--color-surface: #161f2e;
--color-base: #0d1117;
--font-sans: "Montserrat", ui-sans-serif, system-ui, sans-serif;
}
Diese Tokens tauchen dann als normale Utility-Klassen auf, z. B. text-accent
oder bg-surface.
Build: 11ty und Tailwind CLI Hand in Hand
Der Build besteht aus zwei unabhängigen Schritten – Tailwind kompiliert das
CSS, 11ty rendert die Seiten – beide schreiben in dasselbe dist/-Verzeichnis:
"build": "rm -rf dist && npm run build:css && npm run build:eleventy",
"build:css": "tailwindcss -i src/styles/main.css -o dist/styles/main.css --minify",
"build:eleventy": "eleventy"
Tests: Playwright und ein selbstgebauter Kontrast-Check
Die Test-Suite läuft mit Playwright gegen einen echten Produktions-Build. Neben Navigation, Links und Inhalten prüft ein selbstgeschriebener WCAG-Kontrast-Test jeden sichtbaren Textknoten gegen seinen tatsächlichen Hintergrund:
function contrast(fg, bg) {
const l1 = relLuminance(fg);
const l2 = relLuminance(bg);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
So fällt sofort auf, wenn eine neue Farbe (zum Beispiel für das Syntax-Highlighting in diesem Blog) nicht genug Kontrast zum Hintergrund hat.
Deployment: Docker, Gitea Actions und semantic-release
Ein mehrstufiges Dockerfile baut die Seite und liefert sie über nginx aus:
FROM node:24-alpine AS build
WORKDIR /app
COPY . .
RUN npm run build
FROM nginx
COPY /app/dist /usr/share/nginx/html
Jeder Push auf main läuft durch Tests und Build; basierend auf
Conventional Commits ermittelt
semantic-release die nächste Version und setzt bei Bedarf einen Git-Tag.
Ein Tag-Push löst den Deploy-Workflow aus: Docker-Image bauen, in eine
eigene Registry pushen, per SSH auf dem Server den Container neu starten.
Ein vorgeschalteter Caddy-Reverse-Proxy kümmert sich um TLS und leitet
Anfragen an den Container weiter.
Am Ende ist es ein sehr kleines, aber vollständiges System – und genau der richtige Rahmen, um jetzt auch regelmäßiger zu bloggen.