Use when implementing UI components, design systems, or responsive layouts - verifies visual correctness through screenshot comparison and DevTools verification; prevents shipping broken UI
Verifies visual correctness through screenshot comparison and DevTools verification.
npx claudepluginhub bacchus-labs/wranglerThis skill inherits all available tools. When active, it can use any tool Claude has access to.
example.tsVisual regression testing captures screenshots of UI components/pages and compares them against baseline images to detect unintended visual changes.
When to use this skill:
NO UI CHANGES WITHOUT VISUAL VERIFICATION
If you changed UI code (HTML, CSS, JSX, templates):
Visual regression testing integrates with TDD through TWO sequential cycles:
RED Phase:
test('checkout form renders with required fields', async ({ mount }) => {
const component = await mount('<checkout-form></checkout-form>');
// Test functionality (TDD RED - this will fail)
await expect(component.locator('[name="cardNumber"]')).toBeVisible();
await expect(component.locator('[name="expiry"]')).toBeVisible();
await expect(component.locator('[name="cvc"]')).toBeVisible();
});
Run test: FAILS (component doesn't exist)
GREEN Phase:
// Implement checkout-form component
// Add cardNumber, expiry, cvc fields
Run test: PASSES (component renders fields)
REFACTOR Phase: Improve component structure, styling, accessibility
After component functionally works, add visual verification:
test('checkout form visual appearance', async ({ mount, page }) => {
await mount('<checkout-form></checkout-form>');
// Visual regression test
await expect(page.locator('.checkout-form'))
.toHaveScreenshot('checkout-form.png');
});
First run (Baseline Generation):
RED Phase (Visual Regression): After baseline exists, make CSS change:
/* Change button color from blue to red */
.submit-button { background: red; }
Run test: FAILS (screenshot doesn't match baseline) Review diff: Is change intentional?
GREEN Phase (Update Baseline if Intentional): If red button is intentional:
npm test -- --update-snapshots
New baseline committed Run test: PASSES
If red button is NOT intentional (regression):
/* Revert change */
.submit-button { background: blue; }
Run test: PASSES
Integration:
Cross-reference: See practicing-tdd skill for core RED-GREEN-REFACTOR principles.
IF baseline exists (modifying existing UI):
IF no baseline (new UI):
Write tests first (TDD):
// Test that component renders
test('checkout form renders correctly', async ({ page }) => {
await mount('<checkout-form></checkout-form>');
// Take screenshot of component
await expect(page.locator('[data-testid="checkout-form"]'))
.toHaveScreenshot('checkout-form.png');
});
Implement component (GREEN phase)
Prefer element-level over full-page:
// ✅ GOOD: Element-level (less noise)
await expect(page.locator('.checkout-form'))
.toHaveScreenshot('checkout-form.png');
// ❌ BAD: Full page (too much noise)
await expect(page).toHaveScreenshot('entire-page.png');
BEFORE claiming UI works:
Open DevTools Console:
Verify NO errors:
✅ GOOD: Console is empty (or only expected logs)
❌ BAD: Red errors visible
❌ BAD: Yellow warnings visible (unless documented)
Take Console Screenshot:
Check Network Tab:
Test Responsive Breakpoints:
IF baseline exists:
// Test runs, Playwright compares screenshots
// IF different: Test fails with diff image
Review diff image:
Decision tree:
Are differences intentional?
├─ YES → Update baseline, document why
└─ NO → Fix regression, re-run test
IF no baseline:
Baseline files:
tests/
screenshots/
checkout-form.png ← Baseline
checkout-form-diff.png ← Diff (if different)
checkout-form-actual.png ← Actual (if different)
When to update baseline:
Updating baseline:
# Review diff first!
# If intentional, update baseline:
npm test -- --update-snapshots
# Or Playwright specific:
npx playwright test --update-snapshots
test('visual regression', async ({ page }) => {
await page.goto('/checkout');
// Element-level screenshot
await expect(page.locator('.checkout-form'))
.toHaveScreenshot('checkout-form.png', {
maxDiffPixels: 100, // Allow minor differences
});
});
test('visual regression', async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:3000/checkout');
const element = await page.$('.checkout-form');
await element.screenshot({ path: 'checkout-form.png' });
// Compare manually or use Percy/Chromatic
await browser.close();
});
YES (visual tests appropriate):
NO (use other test types):
// playwright.config.ts
export default defineConfig({
expect: {
toHaveScreenshot: {
maxDiffPixels: 100, // Allow minor rendering differences
threshold: 0.2, // 20% threshold for pixel differences
animations: 'disabled', // Disable animations for stability
},
},
});
BEFORE claiming UI work complete:
If ANY checkbox unchecked: UI work is NOT complete.
When claiming UI work complete, provide:
Screenshot evidence:
Screenshot: checkout-form.png (baseline)
[Attach screenshot]
Changes: Intentional (updated button styling)
Baseline updated: YES
DevTools Console evidence:
Console verification:
[Screenshot showing empty console]
Errors: 0
Warnings: 0
Network evidence (if API calls):
Network verification:
[Screenshot showing successful requests]
Expected requests: ✓ GET /api/products
Failed requests: 0
If you catch yourself:
THEN:
Combines with:
| Rationalization | Counter |
|---|---|
| "I can see it looks good" | Your eyes aren't regression tests. Take screenshot. |
| "It's a small change" | Small changes cause visual regressions. Screenshot required. |
| "I'll check it in the browser" | Browser check ≠ automated verification. Take screenshot. |
| "Console errors don't affect appearance" | Errors indicate bugs. Fix before claiming complete. |
| "Full page screenshot is easier" | Element screenshots catch actual changes. Be specific. |
Agent: "I'm implementing a checkout form component."
[Uses frontend-visual-regression-testing skill]
1. Write test expecting checkout form renders
2. Take screenshot of component → baseline
3. Run test → PASS (baseline generated)
4. Refactor CSS for better spacing
5. Run test → FAIL (screenshot different)
6. Review diff → Intentional (better spacing)
7. Update baseline
8. Open DevTools console → 0 errors
9. Take console screenshot
10. Test responsive breakpoints → All look correct
11. Provide evidence in completion message:
- checkout-form.png (baseline)
- Console screenshot (0 errors)
- Responsive screenshots (mobile/tablet/desktop)
"Checkout form complete. Visual regression test passing.
Console clean. Responsive breakpoints verified."
Remember: NO UI CHANGES WITHOUT VISUAL VERIFICATION. Screenshots are evidence, not optional.