# How to Automate Visual Checks Using Screenshots in Archify

> Automate visual checks with screenshots using Archify's visual-check. Programmatically render HTML, capture multi-viewport screenshots, and generate receipts for UI validation.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-08-15

---

**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`](https://github.com/tt-a1i/archify/blob/main/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:

```javascript
// 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`:

```javascript
// 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 discovery** — `findChrome()` checks `ARCHIFY_CHROME` env var or searches standard install paths; returns exit code `2` if unavailable
2. **Side-car preparation** — `sidecarPaths()` 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:

```bash
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:

```javascript
// 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:

```yaml
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.