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

KomponenteZweck
generate-og-image.jsPlaywright-Skript zum Screenshotten von Beiträgen
generate-og-images.ymlGitHub Action ausgelöst bei Beitragsänderungen
baseof.html ÄnderungenBedingte 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: