How to Automate Visual Checks Using Screenshots in Archify

Archify's built-in visual-check utility programmatically renders HTML artifacts in headless Chrome, captures multi-viewport screenshots, and generates structured receipts for automated UI validation.

The visual check automation system in the tt-a1i/archify repository provides a complete, scriptable solution for verifying that HTML diagrams and components render correctly across device sizes and color themes. This guide covers the architecture, execution flow, and practical integration patterns.


How the Visual Check System Works

The implementation lives in archify/bin/visual-check.mjs and consists of four coordinated components:

  • Viewport definitions — Fixed size arrays for containment testing and final screenshot capture
  • Side-car generation — Structured output folder with JSON receipt, contact-sheet HTML, and PNG files
  • Chrome driver — Lightweight CDP client that spawns headless Chrome and controls rendering
  • Orchestration — Main runVisualCheck() function that wires everything together

Viewport Configuration

Two viewport arrays control the automated screenshot capture behavior:

// archify/bin/visual-check.mjs lines 8-13
const VISUAL_CHECK_VIEWPORTS = [
  { width: 1920, height: 1080, name: 'desktop' },
  { width: 1440, height: 900, name: 'laptop' },
  { width: 768, height: 1024, name: 'tablet' },
  { width: 375, height: 667, name: 'mobile' }
];

// archify/bin/visual-check.mjs lines 15-18
const CAPTURE_VIEWPORTS = [
  { width: 1440, height: 900 }  // Primary screenshot size
];

VISUAL_CHECK_VIEWPORTS validates containment (no overflow) across four standard devices. CAPTURE_VIEWPORTS determines which dimensions receive actual PNG screenshots in both light and dark themes.


Side-Car Output Structure

For every processed artifact, sidecarPaths() (lines 54-70) creates a .visual-check folder containing:

File Purpose
*.visual-check.json Machine-readable receipt with metrics and status
*.visual-check.html Human-readable contact-sheet gallery
*.visual-check.{width}x{height}.{theme}.png Actual screenshot files

File naming follows the pattern: diagram.visual-check.1440x900.light.png.


Chrome Driver Implementation

The ChromeVisualBrowser class uses a minimal CDP (Chrome DevTools Protocol) client called PipeCdp:

// From archify/bin/visual-check.mjs lines 35-44, 84-108
class ChromeVisualBrowser {
  async inspect({ url, width, height, theme, screenshot }) {
    // Spawns headless Chrome, forces static UI state,
    // captures metrics, optionally records screenshot
  }
}

The inspect() method:

  • Emulates the requested viewport dimensions
  • Forces light or dark theme via CSS injection
  • Returns containment metrics (innerWidth, scrollWidth, scrollHeight)
  • Writes PNG data when screenshot: true is passed

Execution Flow: Step by Step

The runVisualCheck() function orchestrates the complete visual check automation pipeline:

  1. Chrome discoveryfindChrome() checks ARCHIFY_CHROME env var or searches standard install paths; returns exit code 2 if unavailable
  2. Side-car preparationsidecarPaths() and baseReceipt() initialize output structure
  3. Containment pass — Iterates VISUAL_CHECK_VIEWPORTS to collect overflow metrics (no screenshots)
  4. Screenshot pass — Iterates CAPTURE_VIEWPORTS × 2 themes to write PNG files
  5. Immutability check — Re-hashes original artifact; aborts on mismatch
  6. Result aggregation — Computes containment.status ("pass" or "fail") and builds captures.screenshots array
  7. Atomic writes — Uses writeAtomic() for receipt and contact-sheet; cleans up partial files on failure

CLI Usage for Visual Checks

Run the built-in command directly:

node ./archify/bin/visual-check.mjs ./examples/archify-repo.html

Expected output files:


archify-repo.visual-check.json
archify-repo.visual-check.html
archify-repo.visual-check.1440x900.light.png
archify-repo.visual-check.1440x900.dark.png

Exit codes indicate results:

  • 0 — All viewports contained, screenshots captured successfully
  • 1 — Overflow detected or screenshot failure
  • 2 — Chrome not found (skipped)

Programmatic Visual Check Integration

Import runVisualCheck() for build script automation:

// build-script.mjs
import { runVisualCheck } from './archify/bin/visual-check.mjs';

async function validateArtifacts(files) {
  for (const file of files) {
    const { exitCode, receipt } = await runVisualCheck({
      artifactPath: file,
      // Optional: specify Chrome binary path
      // chromePath: process.env.ARCHIFY_CHROME
    });
    
    if (exitCode !== 0) {
      console.error(`Visual check failed for ${file}`);
      process.exit(1);
    }
    
    console.log(`✓ ${file}: ${receipt.containment.status}`);
  }
}

The returned receipt object contains:

  • receipt.containment.status — "pass" or "fail"
  • receipt.containment.viewports — Array of per-viewport metrics
  • receipt.captures.screenshots — Array with PNG filenames and metadata

CI/CD Integration for Automated Screenshots

GitHub Actions workflow example:

name: Visual Check
on: [push, pull_request]

jobs:
  visual-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Chrome
        uses: browser-actions/setup-chrome@v1
        
      - name: Run visual checks
        run: |
          node ./archify/bin/visual-check.mjs ./examples/archify-repo.html
          
      - name: Verify containment
        run: |
          jq -e '.containment.status == "pass"' \
            archify-repo.visual-check.json

The jq command gates the pipeline: any overflow failure aborts the workflow.


Contact Sheet Generation

The contactSheetHtml() function (invoked during orchestration) produces a minimal HTML gallery displaying:

  • Each screenshot with its viewport dimensions
  • Light/dark theme variants
  • Pass/fail containment labels
  • Links to full-resolution PNG files

Open *.visual-check.html in any browser for immediate visual inspection of automated screenshot results.


Environment Variables

Variable Purpose
ARCHIFY_CHROME Override path to Chrome/Chromium binary
Standard CI Detected automatically; triggers non-interactive output

Summary

  • Archify's visual check automation renders HTML in headless Chrome and captures structured screenshots
  • Four viewport sizes validate containment; one primary size generates theme-variant PNGs
  • Side-car outputs include JSON receipt, HTML contact sheet, and screenshot files
  • Exit codes (0, 1, 2) integrate cleanly with CI pipelines
  • Programmatic API via runVisualCheck() enables custom build workflows

Frequently Asked Questions

How does Archify handle missing Chrome installations?

The findChrome() function first checks the ARCHIFY_CHROME environment variable, then searches common system paths. If Chrome is unavailable, runVisualCheck() returns exit code 2, writes a receipt with status: "skipped", and does not throw—allowing CI pipelines to continue optionally.

Can I customize which viewports get screenshots?

The CAPTURE_VIEWPORTS array in archify/bin/visual-check.mjs (lines 15-18) controls screenshot dimensions. Modify this constant before running the CLI, or call runVisualCheck() from a wrapper script that patches the configuration. Containment validation always uses the full VISUAL_CHECK_VIEWPORTS set.

What triggers a containment failure?

A viewport fails containment when scrollWidth > innerWidth or scrollHeight > innerHeight, indicating content overflow. The receipt's containment.viewports[n].ok field is false for that viewport, and the aggregate containment.status becomes "fail" unless all viewports report ok: true.

How are screenshot files named and organized?

sidecarPaths() generates paths following the pattern {basename}.visual-check.{width}x{height}.{theme}.png alongside {basename}.visual-check.json and {basename}.visual-check.html. All files reside in the same directory as the original artifact, not a separate folder.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →