Facebook-Marketing-Assets: HTML-Canvas-Render-Pipeline statt Photoshop
Wie ich Titelbild und Profilbild für Facebook als HTML-Vorlagen baue und per Node.js + Puppeteer automatisiert rendere – versioniert, reproduzierbar, ohne Adobe.
Das Problem: Marketing-Assets als Blackbox
Letzte Woche stand die Aufgabe an, syfrt.de auf Facebook zu vertreten – mit Titelbild, Profilbild und strukturierten Daten (JSON-LD sameAs). Der klassische Workflow wäre gewesen: Photoshop öffnen, Ebenen verschieben, exportieren, hochladen, beim nächsten Corporate-Design-Update alles manuell wiederholen.
Das widerspricht meinem Prinzip: Infrastruktur als Code, Inhalte als Code, Marketing-Assets als Code. Wenn ich Traefik-Konfigurationen in YAML versioniere und Blogposts in Markdown schreibe, warum sollten dann Facebook-Grafiken als binäre PSD-Dateien in einem Ordner verrotten?
Die Lösung: HTML-Vorlagen für jedes Asset, ein Node.js-Render-Skript mit Puppeteer und ein npm run render-Befehl, der alle Grafiken in konsistenter Qualität ausspuckt.
Die Architektur: HTML + Puppeteer = Reproduzierbare Pixel
Schritt 1: HTML-Vorlagen mit exakten Dimensionen
Facebook verlangt für Titelbilder 820×312 Pixel (Desktop) bzw. 640×360 Pixel (Mobile). Ich habe zwei HTML-Dateien angelegt:
<!-- facebook-cover.html -->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=820, height=312">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
width: 820px;
height: 312px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
display: flex;
align-items: center;
justify-content: center;
font-family: 'Inter', -apple-system, sans-serif;
color: #e0e0e0;
overflow: hidden;
}
.container {
text-align: center;
padding: 2rem;
}
h1 {
font-size: 3.5rem;
font-weight: 700;
margin-bottom: 1rem;
background: linear-gradient(90deg, #00d4ff 0%, #7b2ff7 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
p {
font-size: 1.5rem;
color: #a0a0a0;
letter-spacing: 0.05em;
}
</style>
</head>
<body>
<div class="container">
<h1>syfrt.de</h1>
<p>Webentwicklung · KI-Automatisierung · DevOps</p>
</div>
</body>
</html>
Das Profilbild (180×180 px, kreisrund beschnitten von Facebook) bekommt eine eigene Vorlage mit zentriertem Logo und ausreichend Padding, damit der Kreis-Crop nichts abschneidet.
Schritt 2: Puppeteer-Render-Skript
Puppeteer ist ein Headless-Chrome-Wrapper von Google. Ich nutze ihn, um HTML-Dateien zu laden und als hochauflösende PNG-Dateien zu exportieren:
// render-facebook-assets.js
const puppeteer = require('puppeteer');
const path = require('path');
const fs = require('fs');
const assets = [
{
name: 'facebook-cover',
html: 'facebook-cover.html',
output: 'facebook-cover.png',
width: 820,
height: 312
},
{
name: 'facebook-profile',
html: 'facebook-profile.html',
output: 'facebook-profile.png',
width: 180,
height: 180
}
];
(async () => {
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
for (const asset of assets) {
console.log(`Rendering ${asset.name}...`);
const page = await browser.newPage();
await page.setViewport({
width: asset.width,
height: asset.height,
deviceScaleFactor: 2 // Retina-Qualität
});
const htmlPath = path.resolve(__dirname, asset.html);
await page.goto(`file://${htmlPath}`, {
waitUntil: 'networkidle0'
});
const outputPath = path.resolve(__dirname, 'output', asset.output);
await page.screenshot({
path: outputPath,
omitBackground: false
});
console.log(`✓ Saved to ${outputPath}`);
await page.close();
}
await browser.close();
console.log('\
All assets rendered successfully.');
})();
Wichtige Details:
deviceScaleFactor: 2rendert mit doppelter Auflösung (1640×624 px intern), wird aber auf 820×312 px herunterskaliert → knackig scharfe Grafiken auf Retina-Displays.waitUntil: 'networkidle0'stellt sicher, dass alle Webfonts (Inter von Google Fonts) geladen sind, bevor der Screenshot gemacht wird.omitBackground: falsebehält den CSS-Hintergrund bei (wichtig für Farbverläufe).
Schritt 3: npm-Skript für One-Click-Rendering
In der package.json des Projekts:
{
"scripts": {
"render:facebook": "node render-facebook-assets.js"
},
"devDependencies": {
"puppeteer": "^21.6.1"
}
}
Jetzt reicht ein npm run render:facebook, und alle Assets landen im output/-Ordner – bereit für den Upload.
Integration in die Website: JSON-LD und Footer-Link
Die Facebook-Seite ist jetzt Teil der strukturierten Daten. In der BaseLayout.astro habe ich das JSON-LD-Schema erweitert:
const jsonLd = {
"@context": "https://schema.org",
"@type": "Person",
"name": "Sascha Seyfert",
"url": "https://syfrt.de",
"sameAs": [
"https://github.com/saschaseyfert",
"https://www.facebook.com/syfrt.de"
],
"jobTitle": "Webentwickler, KI-Automatisierung, DevOps"
};
Der Footer zeigt jetzt neben GitHub auch das Facebook-Icon (Lucide facebook-Symbol). Beide Links öffnen in neuem Tab (target="_blank" rel="noopener noreferrer").
Warum dieser Ansatz überlegen ist
1. Versionskontrolle
Jede Änderung am Design ist ein Git-Commit. Ich kann zu jedem Zeitpunkt zurückspringen und sehen, wie das Titelbild vor drei Monaten aussah.
2. Reproduzierbarkeit
Neuer Laptop? git clone, npm install, npm run render:facebook – fertig. Keine Photoshop-Lizenz, keine fehlenden Schriftarten, keine „Wo war nochmal die PSD-Datei?”-Momente.
3. Skalierbarkeit
Neue Plattform (LinkedIn, Twitter, Mastodon)? Neue HTML-Vorlage mit den jeweiligen Dimensionen hinzufügen, im Render-Skript registrieren, fertig. Alle Assets teilen sich die gleichen CSS-Variablen für Farben und Schriftarten.
4. CI/CD-Integration (Zukunft)
Der nächste Schritt wäre, das Render-Skript in die Forgejo-CI-Pipeline zu integrieren. Bei jedem Push auf main könnten die Assets automatisch gerendert und per Webhook an ein CDN oder direkt an die Facebook Graph API geschickt werden.
Lessons Learned: Puppeteer-Fallstricke
Problem 1: Webfonts laden nicht
Symptom: Screenshot zeigt Fallback-Schriftart (Arial) statt Inter.
Lösung: waitUntil: 'networkidle0' statt waitUntil: 'load'. Puppeteer wartet dann, bis alle Netzwerk-Requests (inkl. Google Fonts) abgeschlossen sind.
Problem 2: Farbverläufe sehen pixelig aus
Symptom: Gradient im Hintergrund zeigt Banding-Artefakte.
Lösung: deviceScaleFactor: 2 aktivieren. Puppeteer rendert intern mit doppelter Auflösung und skaliert herunter – glättet Verläufe erheblich.
Problem 3: Transparenter Hintergrund wird schwarz
Symptom: PNG hat schwarzen statt transparenten Hintergrund.
Lösung: omitBackground: true in page.screenshot() UND background: transparent im CSS body-Tag setzen.
Nächste Schritte: Dynamische Daten aus CMS
Aktuell sind Texte und Farben hardcoded in den HTML-Vorlagen. Der nächste Evolutionsschritt wäre, das Render-Skript mit einem Headless CMS (z. B. selbst gehostetes Strapi oder Directus) zu verbinden:
- Marketing-Team pflegt Texte, Farben und Logos im CMS.
- Render-Skript fetcht Daten per API.
- Puppeteer rendert HTML-Template mit dynamischen Werten.
- Assets werden automatisch in Object Storage (Hetzner Storage Box) abgelegt.
Das würde die Pipeline vollständig entkoppeln: Designer arbeiten im CMS, Entwickler pflegen die HTML-Templates, CI/CD orchestriert den Rest.
Fazit: Marketing-Assets gehören ins Git-Repo
Die HTML-Canvas-Render-Pipeline hat sich in der Praxis bewährt. Statt 20 Minuten Photoshop-Fummelei kostet eine Änderung am Titelbild jetzt 30 Sekunden:
- HTML-Datei öffnen.
- Text oder Farbe anpassen.
npm run render:facebookausführen.- PNG hochladen.
Die Grafiken sind pixelgenau, die Ausgabequalität konsistent, und das gesamte Setup passt in 150 Zeilen Code. Kein Vendor-Lock-in, keine Lizenzkosten, keine „Datei ist beschädigt”-Albträume.
Wer seine Infrastruktur ernst nimmt, sollte auch sein Marketing als Code behandeln. Dieser Ansatz ist der Beweis, dass das nicht nur möglich, sondern auch praktikabler ist als der traditionelle Weg.
Commit-Referenz: feat(marketing): Facebook-Titelbild und -Profilbild (HTML-Vorlagen + Render-Skript)
Repository: git.syfrt.de/saschaseyfert/syfrt.de
Render-Output: public/assets/facebook-cover.png, public/assets/facebook-profile.png
Häufige Fragen
Warum HTML und Puppeteer statt Photoshop oder Figma für Social-Media-Grafiken?
HTML-Vorlagen ermöglichen die vollständige Versionierung im Git-Repository, dynamische Daten-Injektion per Script und deterministische Exporte ohne manuelle Klickarbeit oder teure Adobe-Lizenzen.
Wie werden die gerenderten Bilder auf Facebook deployed?
Die generierten Assets werden im CI/CD-Prozess automatisch gebaut, optimiert und können per API oder direkt im Repository für Social-Media-Scheduler bereitgestellt werden.
Fragen zum Thema? Schreiben Sie mir — ich antworte in der Regel innerhalb eines Werktags.