Skip to content

Instantly share code, notes, and snippets.

@jwmatthews
Created January 28, 2026 16:45
Show Gist options
  • Select an option

  • Save jwmatthews/c999742080bde20e360087841b9b4eef to your computer and use it in GitHub Desktop.

Select an option

Save jwmatthews/c999742080bde20e360087841b9b4eef to your computer and use it in GitHub Desktop.

Claude Skill: Visual Regression Testing for Web Applications

Overview

Create a reusable Claude skill that discovers important pages in web applications, captures screenshots before/after code changes, and generates visual comparison reports. Works in headless environments and requires zero modifications to target applications.

Skill Capabilities

The skill will support three main commands:

  1. /screenshot discover - Analyze codebase to find main pages/routes
  2. /screenshot capture [--baseline|--comparison] - Capture screenshots of discovered pages
  3. /screenshot compare - Compare baseline vs comparison screenshots and generate report

Use Case

Validate that UI migrations (PatternFly upgrades, design system changes, refactoring) preserve visual styling across important application pages.

Current Application Context (Example)

  • Application: PatternFly React app at /home/jmatthews.linux/Shared/patternfly_experiments/patternfly-migration-workshop
  • Main pages: Projects, Workloads (with submenus: Pods, Deployments), Storage
  • Routing: State-based (not React Router)
  • Dev server: Vite on port 3000
  • Goal: Capture before PatternFly migration, after migration, and compare

Solution Architecture

Claude Skill with Playwright for browser automation + Pixelmatch for visual diffs

Key Design Principles:

  • Generic: Works with any web application (React Router, state-based routing, etc.)
  • Isolated: Zero modifications to target application
  • Intelligent: Auto-discovers routes and important pages
  • Headless: Runs in VM without GUI
  • Stateful: Remembers what was captured for later comparison

Implementation Steps

1. Create Claude Skill Structure

The skill will be stored in Claude's skills directory with isolated dependencies.

Skill location: ~/.claude/skills/screenshot-visual-regression/

Structure:

~/.claude/skills/screenshot-visual-regression/
├── skill.json              # Skill manifest
├── package.json            # Isolated dependencies
├── node_modules/           # Isolated from target app
├── src/
│   ├── index.ts           # Main skill entry point
│   ├── discover.ts        # Route/page discovery logic
│   ├── capture.ts         # Screenshot capture logic
│   ├── compare.ts         # Comparison and reporting
│   └── helpers/
│       ├── codebase-analyzer.ts    # Analyzes routing patterns
│       ├── page-navigator.ts       # Generic navigation
│       ├── screenshot-utils.ts     # Browser utilities
│       └── state-manager.ts        # Persist discovery metadata
└── .screenshots/          # Created in target app's directory
    ├── .screenshot-config.json    # Auto-generated config
    ├── baseline/
    ├── comparison/
    ├── diff/
    └── reports/

2. Create Skill Manifest

File: ~/.claude/skills/screenshot-visual-regression/skill.json

{
  "name": "screenshot-visual-regression",
  "version": "1.0.0",
  "description": "Discover pages, capture screenshots, and compare visual changes",
  "commands": {
    "discover": {
      "description": "Analyze codebase to find main pages and routes",
      "usage": "/screenshot discover [--url <dev-server-url>]"
    },
    "capture": {
      "description": "Capture screenshots of discovered pages",
      "usage": "/screenshot capture --baseline | --comparison",
      "options": {
        "--baseline": "Capture baseline (before changes)",
        "--comparison": "Capture comparison (after changes)"
      }
    },
    "compare": {
      "description": "Compare baseline and comparison screenshots",
      "usage": "/screenshot compare [--threshold <0.0-1.0>]"
    }
  },
  "dependencies": {
    "playwright": "^1.40.0",
    "pixelmatch": "^5.3.0",
    "pngjs": "^7.0.0"
  }
}

3. Implement Discovery Command

File: ~/.claude/skills/screenshot-visual-regression/src/discover.ts

Discovery Strategy:

  1. Analyze routing configuration (React Router, state-based, file-based, etc.)
  2. Find page components in common locations (pages/, routes/, views/)
  3. Detect navigation structure (menus, sidebars)
  4. Identify interactive states (tabs, modals, expanded sections)
  5. Optionally crawl running dev server to discover dynamic routes
  6. Generate .screenshot-config.json with discovered targets

For current PatternFly app:

  • Parse src/App.tsx for state-based routing
  • Find navigation items in JSX (Nav, NavItem, NavExpandable)
  • Detect page components (ProjectsPage, WorkloadsPage, StoragePage)
  • Find tabs in WorkloadsPage
  • Generate config with 8 screenshot targets

4. Implement Capture Command

File: ~/.claude/skills/screenshot-visual-regression/src/capture.ts

Capture Process:

  1. Load .screenshot-config.json from target app directory
  2. Verify dev server is running (check configured URL)
  3. Launch headless Playwright browser
  4. For each target in config:
    • Navigate using generic navigation helper
    • Wait for page readiness (network idle + selectors)
    • Capture full-page screenshot
    • Save to baseline/ or comparison/ directory
  5. Report summary of captured screenshots

Generic Navigation:

  • Support clicking elements by text, selector, or role
  • Handle expandable menus
  • Support tab switching by index or text
  • Configurable wait strategies per target

5. Implement Compare Command

File: ~/.claude/skills/screenshot-visual-regression/src/compare.ts

Comparison Process:

  1. Load baseline and comparison screenshots
  2. For each screenshot pair:
    • Use pixelmatch to generate pixel diff
    • Calculate difference percentage
    • Generate diff image highlighting changes
  3. Create HTML report with:
    • Summary statistics (identical, different, missing)
    • Side-by-side before/after/diff view
    • Difference percentage for each page
    • Thumbnails for quick overview
  4. Save to reports/comparison-report-{timestamp}.html
  5. Output summary to Claude conversation

6. Implement State Manager

File: ~/.claude/skills/screenshot-visual-regression/src/helpers/state-manager.ts

Manages:

  • .screenshot-config.json in target app directory
  • Stores discovered routes/pages
  • Tracks baseline/comparison capture timestamps
  • Persists dev server URL and settings
  • Allows manual editing of config for fine-tuning

Config Format:

{
  "version": "1.0",
  "devServerUrl": "http://localhost:3000",
  "viewport": { "width": 1920, "height": 1080 },
  "discoveredAt": "2026-01-26T10:00:00Z",
  "baselineCapturedAt": "2026-01-26T10:05:00Z",
  "comparisonCapturedAt": null,
  "targets": [
    {
      "name": "projects-page",
      "description": "Projects page with user profiles",
      "navigation": {
        "type": "click",
        "selector": "nav >> text='Projects'"
      },
      "waitFor": ".pf-v6-c-card",
      "enabled": true
    },
    // ... more targets
  ]
}

7. Implement Codebase Analyzer

File: ~/.claude/skills/screenshot-visual-regression/src/helpers/codebase-analyzer.ts

Analysis Capabilities:

  • React Router v6: Parse routes from JSX/config
  • Next.js: Analyze pages/ or app/ directory structure
  • State-based: Parse component code for routing logic
  • File-based: Detect routing patterns from directory structure
  • Navigation: Extract menu items from common components
  • Interactive Elements: Find tabs, accordions, modals

For PatternFly app specifically:

  • Read src/App.tsx
  • Parse state type: type PageType = 'projects' | 'workloads' | 'storage'
  • Find navigation JSX structure
  • Detect WorkloadsPage has submenus and tabs
  • Generate navigation selectors based on text content

Directory Structure Created

Skill Location (Isolated):

~/.claude/skills/screenshot-visual-regression/
├── skill.json
├── package.json
├── node_modules/              # Skill dependencies (isolated)
└── src/
    ├── index.ts
    ├── discover.ts
    ├── capture.ts
    ├── compare.ts
    └── helpers/
        ├── codebase-analyzer.ts
        ├── page-navigator.ts
        ├── screenshot-utils.ts
        └── state-manager.ts

Target Application (Minimal Additions):

/home/jmatthews.linux/Shared/patternfly_experiments/patternfly-migration-workshop/
├── src/                       # UNTOUCHED
├── package.json               # UNTOUCHED
├── node_modules/              # UNTOUCHED
└── .screenshots/              # Created by skill (gitignored)
    ├── .screenshot-config.json    # Auto-generated
    ├── baseline/
    │   ├── projects-page.png
    │   ├── workloads-pods-details-tab.png
    │   └── ... (8 total)
    ├── comparison/
    │   └── (same structure after migration)
    ├── diff/
    │   └── (diff images)
    └── reports/
        └── comparison-report-{timestamp}.html

Usage Workflow

Phase 1: Discovery (One-time Setup)

# User starts the dev server
$ npm run dev

# User runs discovery in Claude Code CLI
$ /screenshot discover --url http://localhost:3000

What happens:

  • Skill analyzes codebase routing patterns
  • Crawls running app to find pages
  • Generates .screenshots/.screenshot-config.json
  • Shows discovered pages to user for review/editing
  • User can manually edit config to add/remove targets

Phase 2: Capture Baseline (Before Migration)

# Dev server should be running
$ npm run dev

# In Claude Code CLI
$ /screenshot capture --baseline

What happens:

  • Skill loads .screenshots/.screenshot-config.json
  • Launches headless browser
  • Navigates to each discovered page
  • Captures 8 screenshots
  • Saves to .screenshots/baseline/
  • Reports summary to user

Phase 3: Perform Migration

User performs PatternFly migration:

  • Upgrade packages
  • Fix breaking changes
  • Application code/config untouched except for migration

Phase 4: Capture Comparison (After Migration)

# Restart dev server with migrated code
$ npm run dev

# In Claude Code CLI
$ /screenshot capture --comparison

What happens:

  • Skill loads same config from Phase 1
  • Captures screenshots with same navigation paths
  • Saves to .screenshots/comparison/

Phase 5: Compare and Review

# In Claude Code CLI
$ /screenshot compare

What happens:

  • Skill compares baseline vs comparison screenshots
  • Generates pixel diffs for each page
  • Creates HTML report
  • Shows summary in CLI (X identical, Y different)
  • User opens .screenshots/reports/comparison-report-{timestamp}.html to review

Key Implementation Details

Discovery Intelligence:

  • Uses Claude's code analysis to parse routing patterns
  • Detects common frameworks: React Router, Next.js, state-based, file-based
  • Analyzes navigation components to find menu structure
  • Scans page components for interactive elements (tabs, modals)
  • Generates navigation steps as declarative config (not hardcoded scripts)

Generic Navigation Engine:

  • Config-driven navigation (supports any app structure)
  • Navigation types:
    • click: Click element by selector, text, or role
    • navigate: Direct URL navigation
    • sequence: Multiple navigation steps
    • wait: Custom wait conditions
  • Smart waiting: network idle + selector + animations
  • Fallback strategies if navigation fails

Browser Configuration:

  • Headless Chromium (Playwright)
  • Viewport: 1920×1080 (configurable)
  • Args: --no-sandbox, --disable-dev-shm-usage (VM compatibility)
  • DeviceScaleFactor: 1 (consistent screenshots)

Comparison Algorithm:

  • Pixelmatch threshold: 0.1 (configurable via --threshold flag)
  • Generates diff images with highlighted changes
  • Calculates per-page and overall difference percentages
  • Handles missing screenshots gracefully

State Management:

  • Config stored in target app's .screenshots/ directory
  • Automatically gitignored (recommended)
  • Can be committed for team collaboration
  • Timestamps track when captures were made
  • Idempotent: can re-run captures safely

Critical Files to Create

Skill Files (In ~/.claude/skills/screenshot-visual-regression/):

  1. skill.json - Skill manifest defining commands and metadata
  2. package.json - Node.js dependencies (playwright, pixelmatch, pngjs, tsx)
  3. src/index.ts - Main entry point, command router
  4. src/discover.ts - Discovery command implementation
  5. src/capture.ts - Capture command implementation
  6. src/compare.ts - Compare command implementation
  7. src/helpers/codebase-analyzer.ts - Routing pattern detection and parsing
  8. src/helpers/page-navigator.ts - Generic navigation engine
  9. src/helpers/screenshot-utils.ts - Browser utilities and wait strategies
  10. src/helpers/state-manager.ts - Config file management

Generated Files (In target app's .screenshots/ directory):

  1. .screenshot-config.json - Auto-generated by discover command, manually editable
  2. baseline/*.png - Baseline screenshots (created by capture --baseline)
  3. comparison/*.png - Comparison screenshots (created by capture --comparison)
  4. diff/*.png - Visual diff images (created by compare)
  5. reports/*.html - Comparison reports (created by compare)

Critical Files to Modify

Application Files:

  • None - Zero modifications to application code, package.json, or configuration

Recommended .gitignore Addition:

# Visual regression testing
.screenshots/baseline/
.screenshots/comparison/
.screenshots/diff/
.screenshots/reports/
# Keep config for team collaboration:
# !.screenshots/.screenshot-config.json

Verification Steps

Test Skill Installation:

  1. Verify skill directory created at ~/.claude/skills/screenshot-visual-regression/
  2. Check dependencies installed: ls ~/.claude/skills/screenshot-visual-regression/node_modules/
  3. Verify skill available: /screenshot should show in Claude Code autocomplete

Test Discovery Command:

# Navigate to PatternFly app
cd /home/jmatthews.linux/Shared/patternfly_experiments/patternfly-migration-workshop

# Start dev server
npm run dev

# In Claude Code CLI, run discovery
/screenshot discover --url http://localhost:3000

Verify:

  • .screenshots/.screenshot-config.json created in app directory
  • Config contains 8 targets (Projects, Workloads×Pods×3tabs, Workloads×Deployments×3tabs, Storage)
  • Navigation selectors look correct
  • Can manually edit config if needed

Test Baseline Capture:

# Ensure dev server running
/screenshot capture --baseline

Verify:

  • 8 PNG files created in .screenshots/baseline/
  • Files named correctly (projects-page.png, workloads-pods-details-tab.png, etc.)
  • Images render correctly (open one to visually inspect)
  • All pages captured without errors

Test Comparison Capture:

# Make a small CSS change as mock migration
# (e.g., change a color in src/styles/components.css)

# Restart dev server
npm run dev

# Capture comparison
/screenshot capture --comparison

Verify:

  • 8 PNG files created in .screenshots/comparison/
  • Same filenames as baseline
  • Visual differences visible in modified areas

Test Compare Command:

/screenshot compare

Verify:

  • Diff images created in .screenshots/diff/
  • HTML report created in .screenshots/reports/comparison-report-{timestamp}.html
  • CLI shows summary: "X identical, Y different"
  • Open HTML report - shows side-by-side comparisons
  • Report highlights pixel differences correctly

Test Headless VM Environment:

  1. Verify no DISPLAY environment variable: echo $DISPLAY
  2. Run capture command - should work without GUI
  3. If browser fails, install deps: cd ~/.claude/skills/screenshot-visual-regression && npx playwright install-deps chromium

Verify Application Isolation:

  1. Check app's package.json - no new dependencies
  2. Check app's node_modules/ - no playwright/pixelmatch
  3. Only .screenshots/ directory added to app
  4. Skill dependencies isolated in ~/.claude/skills/screenshot-visual-regression/node_modules/

Potential Issues & Solutions

Issue: Browser fails to launch

  • Solution: Run npx playwright install-deps chromium to install system dependencies

Issue: Screenshots are blank/white

  • Solution: Increase wait times or verify dev server is accessible at localhost:3000

Issue: PatternFly components not fully rendered

  • Solution: Adjust wait selectors in screenshot-config.ts or increase timeout values

Issue: False positive diffs in identical content

  • Solution: Increase pixelmatch threshold from 0.1 to 0.2 or higher

Expected Outcome

After implementation, you will have a reusable Claude skill that:

  1. Works with any web application (not just this PatternFly app)
  2. Auto-discovers important pages by analyzing routing patterns
  3. Captures baseline screenshots before code changes
  4. Captures comparison screenshots after code changes
  5. Generates visual diff reports highlighting pixel-level differences
  6. Runs in headless environments without browser GUI
  7. Maintains zero impact on target applications (isolated dependencies)

For this specific PatternFly migration:

  1. Discover 8 important page states automatically
  2. Capture baseline before PatternFly v6+ migration
  3. Perform migration with confidence
  4. Capture comparison after migration
  5. Generate report showing which pages have visual changes
  6. Quickly identify and fix any styling regressions

Future reusability:

  • Use /screenshot discover on any React/Next.js/Vue app
  • Generic enough for different routing patterns
  • Can be used for design system migrations, refactoring, theme changes
  • Config can be shared across team for consistent screenshot targets
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment