Wie dieser Blog entstand: Spec first mit Claude Code
Dieser Blog soll ein Ort für meine Erfahrungen mit Webentwicklung und Claude Code sein. Passend dazu ist er selbst mit Claude Code entstanden – von der leeren Idee bis zur laufenden Seite unter promptandpixel.dev. Und zwar komplett auf meinem Android-Handy, ohne Rechner. Hier ist der Weg dorthin, Schritt für Schritt.
Alles am Handy
Gearbeitet habe ich mit diesen Werkzeugen, alle auf dem Android-Handy:
- Claude Code App: Hier lief die ganze Session. Claude Code hat in einer Cloud-Umgebung geplant, Code geschrieben, Pull Requests geöffnet und gemergt.
- GitHub App und GitHub im Browser: den Browser nur für die Secrets des Deployments.
- Uberspace-Dashboard im Browser und eine SSH-App für die Einrichtung auf dem Server.
Die Domain promptandpixel.dev habe ich erst während der Session registriert, und auch den Uberspace dafür habe ich mittendrin angelegt.
Zuerst die Spezifikation, dann der Code
Am Anfang stand kein Code, sondern eine SPEC.md. Claude Code hat einen ersten Entwurf geschrieben, und alles, was noch nicht entschieden war, landete dort als nummerierte offene Frage. Die habe ich dann nacheinander entschieden: Sprache, Name im Impressum, Lizenz, Gestaltung, Schriften, Hosting.
Einige Entscheidungen haben sich unterwegs geändert. Geplant war zum Beispiel ein zweisprachiger Blog auf Deutsch und Englisch – am Ende ist es erst einmal nur Deutsch geworden. Weil jede Änderung zuerst in der Spec landet, bleibt nachvollziehbar, was wann warum entschieden wurde.
Die Spec selbst ging als Pull Request ins Repository und wurde erst nach meiner Freigabe gemergt. Danach galt sie als Grundlage für alles Weitere.
Warum Astro
Die Anforderungen aus der Spec sind schnell aufgezählt: statische Seiten, Inhalte als Markdown-Dateien, kein JavaScript im Browser, keine externen Ressourcen, kein Tracking. Astro passt dazu gut:
- Es erzeugt beim Build reines HTML, ohne Framework-Laufzeit im Browser.
- Beiträge sind Markdown-Dateien, deren Metadaten über ein Schema geprüft werden. Ein fehlendes Pflichtfeld oder ein unbekanntes Thema lässt den Build fehlschlagen.
- Code-Hervorhebung passiert schon beim Build, nicht im Browser.
Die einzige Stelle mit JavaScript ist der Kopier-Knopf an Code-Blöcken. Ohne JavaScript erscheint er einfach nicht.
Sauberes Markup und handgeschriebenes CSS
Sauberes HTML und CSS sind mir wichtig, deshalb haben sie in der Spec einen eigenen Abschnitt bekommen. Ein paar Folgen daraus:
- Keine Scoped Styles in Astro-Komponenten. Sonst landen
data-astro-cid-*-Attribute im Markup. Alle Styles liegen stattdessen in eigenen CSS-Dateien. - Keine Inline-Styles, auch nicht in Code-Blöcken (mehr dazu im nächsten Abschnitt).
- Lesbares HTML: Die Ausgabe wird nicht minifiziert.
- Natives CSS ohne Framework und ohne Präprozessor, aufgebaut mit Cascade Layers, Design-Tokens als Custom Properties und logischen Properties.
Code-Hervorhebung mit Klassen statt Inline-Styles
Shiki schreibt normalerweise an jedes Token ein style-Attribut mit der Farbe. Der Trick: Ein eigenes Farbschema verwendet nur Platzhalterfarben, die als Kennung dienen. Ein kleiner Transformer ersetzt jede Platzhalterfarbe beim Build durch eine sprechende Klasse und entfernt das style-Attribut. Gekürzt sieht das so aus:
/** Platzhalterfarbe → Klasse. Die Farben dienen nur als Kennung. */
const KLASSEN = {
'#000001': 't-c', // Kommentar
'#000002': 't-k', // Schlüsselwort, @-Regel, Operator-Wort
'#000003': 't-s', // Selektor, Tag, Typ, Klasse
// … weitere Token-Arten
};
export const klassenStattStyles = {
name: 'klassen-statt-styles',
span(node) {
const style = String(node.properties.style ?? '');
delete node.properties.style;
const farbe = /color:\s*(#[0-9a-f]{6})/i.exec(style)?.[1]?.toLowerCase();
if (farbe && KLASSEN[farbe]) fuegeKlasseHinzu(node, KLASSEN[farbe]);
},
// … weitere Hooks: markierte Zeilen, Diff, Dateiname als <figcaption>
};Die echten Farben stehen im Stylesheet als Custom Properties. Die Code-Blöcke in diesem Beitrag laufen genau durch diesen Transformer.
Prüfungen bei jedem Pull Request
Damit das so bleibt, prüft ein Build-Check bei jedem Pull Request Formatierung, CSS, Typen, Build, gültiges HTML und eine eigene Inhaltsprüfung – etwa auf externe Ressourcen, kaputte Links oder versehentlich veröffentlichte Zugangsschlüssel.
Das Design: drei Vorschläge, ein HTML
Für die Gestaltung hat Claude Code drei Stilrichtungen als Vorschauseiten gebaut: typografisch, im Terminal-Look und verspielt. Alle drei nutzen exakt dasselbe HTML, nur das CSS unterscheidet sich.
Gewonnen hat die typografische Variante, und zwar nur im dunklen Modus. Danach kam eine zweite Vorschau mit drei Schriftpaaren. Ausgewählt habe ich Literata für den Text, Inter für Navigation und Metadaten und JetBrains Mono für Code – ohne Code-Ligaturen. Alle drei stehen unter der SIL Open Font License und werden selbst ausgeliefert, nicht über einen externen Dienst.
Inzwischen habe ich die Optik noch einmal gewechselt: Der Blog sieht jetzt aus wie das Astro-Theme AstroPaper, aber mit dunkelgrauem Hintergrund und durchgehend in JetBrains Mono.
Datenschutz, Lizenz und Suchmaschinen
Der Blog setzt keine Cookies, nutzt kein Tracking und lädt nichts von fremden Servern. Für Suchmaschinen und Link-Vorschauen gibt es trotzdem alles Nötige: Titel, Beschreibungen, Canonical-Links, Open-Graph-Angaben und strukturierte Daten.
Alle eigenen Inhalte stehen unter CC0 1.0. Wer etwas davon brauchen kann, darf es ohne Bedingungen verwenden.
Beiträge per Chat
Beiträge entstehen im Gespräch: Ich liefere Inhalt und Stichpunkte, Claude Code fragt fehlende Angaben ab, schreibt einen Entwurf und zeigt mir den vollständigen Text. Erst nach meinem OK wird veröffentlicht. Dafür gibt es eigene Skills im Repository und ein Skript, das einen Pull Request öffnet, auf den Build-Check wartet und nur bei grünem Ergebnis mergt.
Hosting auf Uberspace
Gehostet wird der Blog bei Uberspace, auf einem eigenen Uberspace nur für den Blog.
Die Domain promptandpixel.dev ist bei INWX registriert; die DNS-Einträge habe ich dort ebenfalls vom Handy aus gesetzt. Weil .dev-Domains im Browser immer HTTPS erzwingen, war die Seite erst erreichbar, nachdem Uberspace das Zertifikat ausgestellt hatte.
Das Deployment übernimmt eine GitHub Action: Sie baut die Seite, prüft sie und lädt sie per rsync hoch. Der dafür verwendete Schlüssel darf auf dem Server ausschließlich in den Webordner schreiben – eine Shell bekommt er nicht. Eingerichtet habe ich ihn per SSH-App direkt auf dem Server, den privaten Teil habe ich als GitHub-Secret hinterlegt.
Zum Schluss kamen zwei Server-Regeln dazu, die ich vorher direkt auf dem Server getestet habe: eine echte 404-Seite mit dem Status 404 und eine Weiterleitung von www.promptandpixel.dev auf die Adresse ohne www. Die .htaccess wird bei jedem Deployment mit ausgeliefert:
# Echte 404-Seite mit Status 404
ErrorDocument 404 /404.html
# Kanonischer Host ohne www (301)
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.promptandpixel\.dev$ [NC]
RewriteRule ^ https://promptandpixel.dev%{REQUEST_URI} [R=301,L]Wie lange es gedauert hat
Das leere Repository habe ich um 19:02 Uhr angelegt, dieser Beitrag ging um 21:52 Uhr online – knapp drei Stunden. In dieser Zeit habe ich nebenbei auch die Domain bei INWX registriert und die DNS-Einträge gesetzt sowie den Uberspace angelegt und eingerichtet.
Der Stand
Der Blog läuft, vorerst im Vorschaubetrieb: Suchmaschinen sind noch ausgesperrt, bis die letzten Seitentexte stehen. Jede Änderung geht als Pull Request durch den Build-Check und ist nach dem Merge automatisch online. Was noch fehlt, sind vor allem Texte – und weitere Beiträge.