Rich Links für Faule Entwickler
Wenn ich einen Blog-Link auf Twitter oder Slack teile, wird er als reiner Text angezeigt. Kein Vorschaubild. Nur eine URL wie granda.org/en/2026/01/02/claude-code-on-the-go/.
Ich brauchte Open Graph Bilder. Der Standardansatz: manuell ein 1200x630 Bild für jeden Beitrag erstellen. Das ist mühsam. Ich bat Claude, es zu automatisieren.
flowchart LR
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
flowchart TB
Push[git push] --> GHA[GitHub Actions]
GHA --> Hugo[Hugo Server]
GHA --> PW[Playwright]
PW -->|screenshot| Hugo
PW --> IMG[OG Image]
IMG --> Commit[git commit]
Commit --> Deploy[Deploy]
Die Einrichtung
Ich erklärte Claude das Problem: Beiträge benötigen Social-Preview-Bilder, aber ich möchte sie nicht manuell erstellen. Screenshot des Artikelinhalts machen, als OG-Bild speichern, Frontmatter automatisch aktualisieren.
Was Claude Gebaut Hat
| Komponente | Zweck |
|---|---|
generate-og-image.js | Playwright-Skript zum Screenshotten von Beiträgen |
generate-og-images.yml | GitHub Action ausgelöst bei Beitragsänderungen |
baseof.html Änderungen | Bedingte og:image Meta-Tags |
Das Playwright-Skript startet headless Chrome, navigiert zum Beitrag, erzwingt den hellen Modus, blendet Header und Footer aus und macht einen Screenshot bei 1200x630:
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
viewport: { width: 1200, height: 630 },
deviceScaleFactor: 2, // Retina for crisp text
});
// Force light mode, hide nav
await page.evaluate(() => {
document.documentElement.setAttribute("data-theme", "light");
document.querySelector("header").style.display = "none";
document.querySelector("footer").style.display = "none";
});
await page.screenshot({ path: outputPath, type: "png" });
Die GitHub Action wird bei jeder .en.md Dateiänderung ausgelöst, startet Hugo, führt das Skript aus und committet das generierte Bild zurück ins Repo:
on:
push:
branches: [main]
paths:
- "content/posts/**/*.md"
steps:
- name: Start Hugo server
run: hugo server --bind 0.0.0.0 --port 1313 &
- name: Generate OG images
run: node scripts/generate-og-image.js "$file"
- name: Commit changes
run: |
git add static/images/posts/ content/posts/
git commit -m "Generate Open Graph images for blog posts"
git push
Das Template schließt das Bild bedingt ein, wenn das Frontmatter ein image Feld hat:
{{- if .Params.image -}}
<meta property="og:image" content="{{ $canonical }}{{ .Params.image }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="{{ $canonical }}{{ .Params.image }}">
{{- else -}}
<meta name="twitter:card" content="summary">
{{- end -}}
404: Slug Nicht Gefunden
Erster Durchlauf. Playwright hat eine leere Seite gescreenshottet. Die URL gab 404 zurück.
Das Skript hatte die URL aus dem Dateinamen erstellt: automatic-blog-translations.en.md → /en/2025/12/23/automatic-blog-translations/
Aber Hugo verwendet nicht den Dateinamen für Slugs. Es verwendet den Titel. Die tatsächliche URL war /en/2025/12/23/automatic-blog-translations-with-claude-and-github-actions/.
Ich hätte Hugos slugify-Algorithmus in JavaScript neu implementieren können. Stattdessen: Hugo fragen.
const output = execSync("hugo list all", { encoding: "utf8" });
// CSV output includes the exact permalink Hugo generates
Eine Zeile. Keine Edge Cases. Hugo kennt seine eigenen URLs bereits.
Was als Nächstes Kommt
Die aktuelle Implementierung generiert nur Bilder für englische Beiträge. Übersetzte Versionen verweisen auf denselben Bildpfad, was funktioniert, aber es bedeutet, dass das OG-Bild immer englischen Text zeigt, auch wenn eine spanische oder japanische Version geteilt wird.
Echte i18n-Unterstützung kommt im nächsten PR. Vorerst bekommt jeder Beitrag ein Social-Bild, was besser ist als keins.
Die Erkenntnis
Das dauerte 14 Minuten zum Bauen und Debuggen. Jetzt bekommt jeder Beitrag automatisch ein Social-Preview-Bild. Keine manuelle Arbeit pro Beitrag. Die Automatisierung amortisiert sich nach einer Handvoll Beiträgen.
Die funktionierende Version ausliefern, die Lücken entdecken, iterieren.
Verwandte Beiträge:
- Automatische Blog-Übersetzungen mit Claude und GitHub Actions - gleicher Stack, andere Automatisierung
- Mein QA-Ingenieur ist ein LLM - Claude den Code überprüfen lassen, den es schreibt
- Meinen Eigenen Newsletter mit Claude Erstellen - ein weiterer 30-Minuten-Build