Fähigkeit: Visueller Vergleich (Visual Diff)
Vergleichen Sie zwei URLs visuell – eine Basis (z. B. Produktionsseite) und eine aktuelle (z. B. lokaler Entwicklungsserver oder Storybook-Story) – um pixelgenaue Unterschiede zu finden und Korrekturen zu steuern.
Wann verwenden
- Nachbau einer vorhandenen Seite oder eines Abschnitts in Storybook oder der Astro-Seite
- Überprüfung der visuellen Genauigkeit einer Komponente im Vergleich zu einem Referenzdesign
- Kontrolle der responsiven Wiedergabetreue über Breakpoints hinweg nach Stiländerungen
- Iteratives Anpassen von CSS/HTML, bis eine Komponente ihrer Referenz entspricht
Wann NICHT verwenden
- Nur Inhaltsänderungen (Textaktualisierungen, Textkorrekturen), bei denen das Layout unverändert bleibt
- Vergleiche von Designs aus Figma – verwenden Sie stattdessen die Figma MCP-Fähigkeit
- Leistungs- oder Barrierefreiheits-Audits – verwenden Sie Lighthouse oder das a11y-Addon
Voraussetzungen
Stellen Sie vor dem Start sicher:
- Playwright MCP ist in
.vscode/mcp.jsonmit--caps=visionkonfiguriert - pixelmatch und pngjs sind installiert: Überprüfen Sie die
devDependenciesinpackage.json - Die Basis-URL ist erreichbar (externe Seite oder ein laufender Server)
- Die aktuelle URL ist erreichbar (Entwicklungsserver auf
:4321, Storybook auf:6006usw.) - Das
temp/-Verzeichnis existiert im Workspace-Root (es ist in gitignore)
Wenn der Entwicklungsserver oder Storybook nicht läuft, starten Sie diese über die VS Code-Aufgaben:
- Entwicklungsserver: Verwenden Sie die Dev Server-Aufgabe
- Storybook: Verwenden Sie die Storybook-Aufgabe
Arbeitsablauf
Schritt 1: Eingaben empfangen
Der Agent benötigt:
| Eingabe | Erforderlich | Beispiel |
|---|---|---|
| Basis-URL | Ja | https://www.bitovi.com/ |
| Aktuelle URL | Ja | http://localhost:4321/ oder http://localhost:6006/?path=/story/card--default |
| CSS-Selektor | Nein | #hero-section oder .card-grid (für elementbezogenen Vergleich) |
| Breakpoints | Nein | Standard: Mobil (375×667), Tablet (768×1024), Desktop (1280×720) |
Schritt 2: Screenshot der Basis erstellen
Komponentenbezogener Vergleich (bevorzugt beim Vergleich einer einzelnen Komponente)
Beim Vergleich einer bestimmten Komponente – insbesondere wenn die Storybook-Story nur diese Komponente rendert, die Basis-Seite jedoch eine gesamte Seite darum herum hat – beschneiden Sie auf den Begrenzungsrahmen der Komponente, damit der Diff nicht durch nicht verwandte Seitenabschnitte (Navigationsleisten, fixierte Overlays, andere Module darüber/darunter) verunreinigt wird.
So beschneiden Sie auf der Basis-Seite:
- Navigieren Sie zur Basis-URL
- Ändern Sie die Viewport-Größe auf
{ width: 1280, height: 720 } - Scrollen Sie das Zielelement in den sichtbaren Bereich:
() => document.querySelector('<selector>').scrollIntoView({ behavior: 'instant', block: 'start' }) - Messen Sie die gerenderte Höhe der Komponente:
() => document.querySelector('<selector>').getBoundingClientRect().height - Ändern Sie die Viewport-Höhe auf genau diese Höhe (Breite bleibt konstant):
mcp_playwright_browser_resize → { width: 1280, height: <gemessene Höhe> } - Erstellen Sie den Screenshot – er erfasst jetzt nur den Bereich der Komponente.
Warnung zum Hintergrunddurchscheinen: Was hinter der Komponente sichtbar ist, unterscheidet sich zwischen Storybook (leer) und der Basis-Seite (andere Module). Häufige Fälle: klebende/lokale Navigationsleisten, die oben überlappen, Modals/Overlays mit anderem Seiteninhalt dahinter und teilweise transparente Komponenten. Diese residualen Diffs gruppieren sich außerhalb des eigentlichen Inhalts der Komponente und sind nicht umsetzbar – ignorieren Sie sie.
So beschneiden Sie auf der Storybook-Story:
Storybooks iframe.html rendert nur die Komponente ohne umgebendes Chrome – kein Größenanpassung nötig. Navigieren Sie einfach und erstellen Sie den Screenshot:
mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=<story-id>&viewMode=story" }
mcp_playwright_browser_resize → { width: 1280, height: <gleiche Höhe wie beim Basis-Beschnitt> }
mcp_playwright_browser_take_screenshot → { filename: "temp/vdiff-current-desktop.png" }
Vollbild-Viewport-Screenshot (beim Vergleich vollständiger Seiten)
1. mcp_playwright_browser_navigate → { url: "<basis-url>" }
2. mcp_playwright_browser_resize → { width: 1280, height: 720 }
3. mcp_playwright_browser_wait_for → { time: 2 } # Seite vollständig rendern lassen
4. mcp_playwright_browser_take_screenshot → {
type: "png",
filename: "temp/vdiff-baseline-desktop.png"
}
Für jeden Breakpoint wiederholen:
| Breakpoint | Breite | Höhe | Dateinamen-Suffix |
|---|---|---|---|
| Mobil | 375 | 667 | -mobile |
| Tablet | 768 | 1024 | -tablet |
| Desktop | 1280 | 720 | -desktop |
Schritt 3: Screenshot der aktuellen Seite erstellen
Wiederholen Sie den exakt gleichen Prozess für die aktuelle URL, mit Dateinamen wie temp/vdiff-current-{breakpoint}.png.
Wichtig: Verwenden Sie auf beiden Seiten die gleichen Viewport-Dimensionen und die gleiche Beschneidungsstrategie (komponentenbezogen oder Vollbild), damit der Vergleich Äpfel mit Äpfeln ist.
Schritt 4: Diff-Skript ausführen
Für jeden Breakpoint führen Sie das visuelle Diff-Skript im Terminal aus:
node scripts/visual-diff.mjs \
--baseline temp/vdiff-baseline-desktop.png \
--current temp/vdiff-current-desktop.png \
--output temp/vdiff-diff-desktop.png
Das Skript gibt JSON auf stdout aus:
{
"totalPixels": 921600,
"diffPixels": 4521,
"diffPercent": 0.49,
"width": 1280,
"height": 720,
"baselineDimensions": { "width": 1280, "height": 720 },
"currentDimensions": { "width": 1280, "height": 720 },
"dimensionsMismatch": false,
"diffImagePath": "/absoluter/pfad/zu/temp/vdiff-diff-desktop.png"
}
Wenn dimensionsMismatch true ist, hatten die Bilder unterschiedliche Größen. Das Skript füllt das kleinere Bild mit transparenten Pixeln auf, bevor es vergleicht. Dies deutet oft auf ein Layout-Problem hin, das untersucht werden sollte.
Schritt 5: Diff-Bild anzeigen
Starten Sie einen temporären HTTP-Server, um das temp/-Verzeichnis auszuliefern, und navigieren Sie dann mit Playwright zum Diff-Bild:
# Server im Hintergrund starten (einmal pro Sitzung ausführen)
npx -y http-server temp/ -p 8787 --cors -c-1 &
Navigieren Sie dann mit Playwright zum Diff-Bild:
mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-desktop.png" }
mcp_playwright_browser_take_screenshot → {} # löst vision aus – der Agent kann jetzt das Diff sehen
Das --caps=vision-Flag bei Playwright MCP bedeutet, dass der Agent das Bild direkt in der Screenshot-Ausgabe sehen kann.
Hinweis: file://-URLs werden von Playwright MCP blockiert, daher ist der HTTP-Server erforderlich. Starten Sie ihn einmal und verwenden Sie ihn für alle Diff-Bilder in der Sitzung wieder.
Diff-Bild lesen:
- Rote Pixel = Unterschiede zwischen Basis und aktueller Seite
- Gelbe Pixel = Anti-Aliasing-Unterschiede (normalerweise ignorierbar)
- Abgedunkeltes Original = Bereiche, die übereinstimmen (bei 30% Deckkraft für Kontext)
- Große rote Cluster = strukturelle Probleme (fehlende Elemente, Layout-Verschiebungen, falsche Größen)
- Verstreute rote Punkte = Subpixel-Rendering, Schriftglättung oder Anti-Aliasing (normalerweise akzeptabel)
- Rote Bänder an den Rändern = Maßunterschiede oder Unterschiede bei Padding/Margin
Schritt 6: Interpretieren und handeln
Kombinieren Sie die quantitativen JSON-Daten mit der visuellen Diff-Inspektion:
| diffPercent | Interpretation | Aktion |
|---|---|---|
| < 1% | Visuell identisch | Nur minimale Subpixel-Unterschiede. Keine Aktion erforderlich. |
| 1–5% | Gute Übereinstimmung | Diff-Bild auf Abstands-, Schriftstärke- oder Randunterschiede prüfen. Kleine CSS-Anpassungen wahrscheinlich nötig. |
| 5–15% | Deutliche Unterschiede | Layout-Verschiebungen, Farbunterschiede oder fehlende Elemente. Überprüfen Sie die roten Cluster im Diff, um zu identifizieren, welche Abschnitte Arbeit benötigen. |
| > 15% | Erhebliche Abweichung | Große strukturelle oder stilistische Unterschiede. Konzentrieren Sie sich zuerst auf die größten roten Regionen – diese zeigen die größten Layout-Diskrepanzen. |
Beim Interpretieren des Diff-Bildes beachten:
- Wo auf der Seite befinden sich die roten Cluster? (oben, Mitte, unten, links, rechts)
- Sind sie mit bestimmten Elementen verbunden? (Navigation, Hero, Karten, Footer)
- Deuten die Cluster auf Abstandsprobleme, Farbunterschiede oder fehlende Inhalte hin?
Schritt 7: Korrigieren und erneut vergleichen
- Nehmen Sie CSS/HTML-Korrekturen basierend auf der Diff-Analyse vor
- Wiederholen Sie die Schritte 3–6 (nur die aktuelle URL erneut screenshoten)
- Fahren Sie fort, bis
diffPercentein akzeptables Niveau erreicht
Typische Zielwerte:
- Exakte Nachbildung: < 2%
- Angemessene Übereinstimmung: < 5%
- Strukturelle Übereinstimmung (unterschiedlicher Inhalt): < 15%
Vollbild-Screenshots
Standardmäßig erfassen Screenshots nur den sichtbaren Viewport. Für lange Seiten verwenden Sie die fullPage-Option:
mcp_playwright_browser_take_screenshot → {
type: "png",
fullPage: true,
filename: "temp/vdiff-baseline-desktop-full.png"
}
Warnung: Vollbild-Screenshots erzeugen große Bilder und langsamere Diffs. Nur verwenden, wenn Sie gesamte Seitenlayouts vergleichen.
Storybook-spezifische Tipps
Beim Screenshot von Storybook-Stories:
-
Verwenden Sie die iframe-URL für sauberere Screenshots (kein Storybook-Chrome):
http://localhost:6006/iframe.html?id=components-card--default&viewMode=story -
Auf Rendern warten – Storybook-Stories benötigen möglicherweise einen Moment zur Hydratation:
mcp_playwright_browser_wait_for → { time: 3 } -
Element-Screenshots eignen sich gut, um den Story-Inhalt von eventuellem Storybook-Padding zu isolieren.
Schwellenwertanpassung
Das --threshold-Flag (0 bis 1) steuert die Empfindlichkeit von pixelmatch:
| Wert | Empfindlichkeit | Anwendungsfall |
|---|---|---|
| 0.05 | Sehr streng | Exakter Pixelabgleich, erfasst alles |
| 0.1 | Standard | Gute Balance, ignoriert die meisten Anti-Aliasing-Artefakte |
| 0.2 | Großzügig | Toleriert Unterschiede im Schrift-Rendering zwischen Systemen |
| 0.3 | Sehr großzügig | Erfasst nur größere Farb-/Layout-Unterschiede |
node scripts/visual-diff.mjs \
--baseline temp/vdiff-baseline-desktop.png \
--current temp/vdiff-current-desktop.png \
--output temp/vdiff-diff-desktop.png \
--threshold 0.2
Beispiel: Vergleich eines Hero-Abschnitts
# 1. Screenshot der Basis (Produktionsseite Hero)
mcp_playwright_browser_navigate → { url: "https://www.bitovi.com/" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 2 }
mcp_playwright_browser_snapshot → {}
# Finden Sie die Referenz für den Hero-Abschnitt, z.B. ref="hero-[1]"
mcp_playwright_browser_take_screenshot → {
type: "png",
element: "hero section",
ref: "hero-[1]",
filename: "temp/vdiff-baseline-hero-desktop.png"
}
# 2. Screenshot der aktuellen Seite (Storybook-Story)
mcp_playwright_browser_navigate → { url: "http://localhost:6006/iframe.html?id=components-hero--default" }
mcp_playwright_browser_resize → { width: 1280, height: 720 }
mcp_playwright_browser_wait_for → { time: 3 }
mcp_playwright_browser_take_screenshot → {
type: "png",
filename: "temp/vdiff-current-hero-desktop.png"
}
# 3. Diff ausführen (im Terminal)
node scripts/visual-diff.mjs \
--baseline temp/vdiff-baseline-hero-desktop.png \
--current temp/vdiff-current-hero-desktop.png \
--output temp/vdiff-diff-hero-desktop.png
# 4. Temporären Server starten (einmal pro Sitzung, im Hintergrund-Terminal)
npx -y http-server temp/ -p 8787 --cors -c-1 &
# 5. Diff-Bild über Playwright anzeigen
mcp_playwright_browser_navigate → { url: "http://localhost:8787/vdiff-diff-hero-desktop.png" }
mcp_playwright_browser_take_screenshot → {} # löst vision aus
# 6. Interpretieren: JSON-Ausgabe lesen + Diff-Bild visuell prüfen
# 7. Stile korrigieren, aktuelle Seite erneut screenshoten, erneut diffen
Aufräumen
Alle Ausgabedateien werden im temp/-Verzeichnis abgelegt, das in gitignore steht. So räumen Sie nach einer Sitzung auf:
rm -f temp/vdiff-*.png