css-visual-diff

module
v0.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 27, 2026 License: MIT

README

css-visual-diff

css-visual-diff is a Go CLI for comparing rendered HTML/CSS across two browser targets. It is being rebuilt from the proven sbcap comparison engine and now uses a standard go-go-golems project layout.

Current shape

The live tool now centers on:

  • browser-driven capture via chromedp
  • element-level compare workflows
  • computed CSS diffs
  • matched-style / cascade inspection
  • pixel diff artifacts

The previous Python prototype has been preserved under:

  • legacy/python-prototype/

Development

GOWORK=off go mod tidy
GOWORK=off go test ./...
GOWORK=off go build ./cmd/css-visual-diff

CLI

GOWORK=off go run ./cmd/css-visual-diff --help
GOWORK=off go run ./cmd/css-visual-diff compare --help
GOWORK=off go run ./cmd/css-visual-diff chromedp-probe --help

JavaScript verbs and programmable catalogs

css-visual-diff can scan annotated JavaScript files and expose them as CLI verbs under the lazy verbs namespace:

GOWORK=off go run ./cmd/css-visual-diff verbs --help
GOWORK=off go run ./cmd/css-visual-diff verbs catalog inspect-page --help

Built-in workflow verbs include:

# Compare one region through the script-backed compare workflow.
GOWORK=off go run ./cmd/css-visual-diff verbs script compare region \
  --leftUrl http://localhost:3000/original \
  --rightUrl http://localhost:3000/react \
  --leftSelector '#cta' \
  --outDir /tmp/cssvd-compare

# Inspect one URL/selector into a Go-backed catalog manifest and index.
GOWORK=off go run ./cmd/css-visual-diff verbs catalog inspect-page \
  http://127.0.0.1:8767/ '#cta' /tmp/cssvd-page \
  --slug cta \
  --artifacts css-json \
  --output json

# Inspect selectors from an existing YAML config into a catalog.
GOWORK=off go run ./cmd/css-visual-diff verbs catalog inspect-config \
  examples/pyxis-atoms-prototype-vs-storybook.yaml react /tmp/cssvd-config \
  --artifacts css-json \
  --output json

External verb repositories can be supplied at runtime:

GOWORK=off go run ./cmd/css-visual-diff verbs --repository examples/verbs examples catalog inspect-page \
  http://127.0.0.1:8767/ '#cta' /tmp/cssvd-example \
  --output json

For repeatable project workflows, check in a local repository config:

# .css-visual-diff.yml
verbs:
  repositories:
    - name: project
      path: ./verbs

Relative paths are resolved from the config file that declares them, so ./verbs still points at the project repository when commands are run from nested directories. Use .css-visual-diff.override.yml for private local repositories and keep it gitignored.

JavaScript verbs can use the Promise-first native module:

const cvd = require("css-visual-diff")
const browser = await cvd.browser()
const page = await browser.page(url, { viewport: { width: 1280, height: 720 } })
const statuses = await page.preflight([{ name: "cta", selector: "#cta" }])
const result = await page.inspectAll([{ name: "cta", selector: "#cta", props: ["color"] }], {
  outDir: "/tmp/cssvd/artifacts/cta",
  artifacts: "css-json",
})

See the embedded Glazed help entries:

css-visual-diff help javascript-api
css-visual-diff help javascript-verbs
css-visual-diff help pixel-accuracy-scripting-guide

These entries cover require("css-visual-diff"), Promise behavior, locators, extraction, snapshots, diffs, typed errors, catalog APIs, __verb__, repositories, generated flags, output modes, duplicate command paths, and migration notes.

Migration note: generated JavaScript commands are intentionally no longer injected at the CLI root. Use css-visual-diff verbs ... so repository scan errors and duplicate script command paths remain scoped to the verbs subtree.

Run co-located configs from a directory

You can keep small comparison configs next to the component or page they cover and run all of them through the existing run verb:

GOWORK=off go run ./cmd/css-visual-diff run \
  --config-dir web/packages/pyxis-components/src \
  --dry-run

--config-dir scans recursively for:

*.css-visual-diff.yml
*.css-visual-diff.yaml

It intentionally does not load every YAML file, and it skips common generated/vendor directories such as node_modules, .git, dist, build, and .css-visual-diff. Use explicit --config path/to/file.yaml when you want to run one config file.

Inspect one side before comparing

When tuning a *.css-visual-diff.yml file, first inspect one side and one selector before running a full comparison. This helps verify that the URL, viewport, prepare hook, root selector, section selector, and CSS probe selector are correct.

A config can contain many screenshot regions under sections and many computed-CSS probes under styles. Inspect a named CSS probe and write a bundle of artifacts:

GOWORK=off go run ./cmd/css-visual-diff inspect \
  --config examples/pyxis-atoms-prototype-vs-storybook.yaml \
  --side react \
  --style button-primary \
  --out /tmp/css-visual-diff-inspect/button-primary

The bundle contains:

metadata.json
screenshot.png
prepared.html
computed-css.json
computed-css.md
inspect.json

For tight selector-tuning loops, use the single-artifact verbs:

# Check the crop/element screenshot.
GOWORK=off go run ./cmd/css-visual-diff screenshot \
  --config examples/pyxis-atoms-prototype-vs-storybook.yaml \
  --side react \
  --style button-primary \
  --output-file /tmp/button-primary.png

# Check computed CSS in human-readable Markdown.
GOWORK=off go run ./cmd/css-visual-diff css-md \
  --config examples/pyxis-atoms-prototype-vs-storybook.yaml \
  --side react \
  --style button-primary \
  --output-file /tmp/button-primary-css.md

# Debug prepared DOM/root output.
GOWORK=off go run ./cmd/css-visual-diff html \
  --config examples/pyxis-atoms-prototype-vs-storybook.yaml \
  --side original \
  --root \
  --output-file /tmp/original-root.html

Available single-artifact verbs are screenshot, css-md, css-json, html, and inspect-json. They share the same selector flags as inspect: --root, --section, --style, or --selector.

Prepared targets

Some design sources do not render the comparison target directly. They may open a pan/zoom design canvas, prototype shell, or development board first. Config-driven runs can now prepare each target after navigation and before capture/CSS analysis.

Minimal script prepare:

original:
  name: prototype
  url: http://localhost:7070/Pyxis%20Public%20Site.html
  wait_ms: 1000
  viewport: { width: 1200, height: 2200 }
  root_selector: "#capture-root"
  prepare:
    type: script
    wait_for: "window.React && window.ReactDOM && window.PPXDesktop"
    script: |
      document.body.innerHTML = '<div id="capture-root"></div>';
      document.body.style.margin = '0';
      document.body.style.background = '#fff';
      const root = document.getElementById('capture-root');
      root.style.width = '920px';
      ReactDOM.createRoot(root).render(React.createElement(PPXDesktop, { page: 'shows' }));
    after_wait_ms: 1000

Convenience React-global prepare:

original:
  name: prototype
  url: http://localhost:7070/Pyxis%20Public%20Site.html
  viewport: { width: 1200, height: 2200 }
  prepare:
    type: direct-react-global
    wait_for: "window.React && window.ReactDOM && window.PPXDesktop"
    component: PPXDesktop
    props: { page: shows }
    root_selector: "#capture-root"
    width: 920
    background: "#fff"

When root_selector is set, capture mode uses an element screenshot for the full baseline PNG instead of a browser full-page screenshot. This avoids including prototype shell or design-canvas chrome in the exported baseline.

Optional capture artifacts and validation:

sections:
  - name: full
    selector_original: "#capture-root"
    selector_react: "[data-page='shows']"
    expect_text_original:
      includes: ["ppxis", "Upcoming shows", "Instagram"]
      excludes: ["01 · Desktop", "Poster-grid shell"]
    expect_png_original:
      width: 920
      min_height: 1700
      top_strip_near: { rgb: [255, 255, 255], tolerance: 8 }
      top_strip_not_near: { rgb: [240, 238, 233], tolerance: 8 }

output:
  dir: ./out/pyxis-public-shows
  write_json: true
  write_markdown: true
  write_pngs: true
  write_prepared_html: true
  write_inspect_json: true
  validate_pngs: true

To generate a static artifact browser for the run, include html-report after the modes that produce artifacts:

GOWORK=off go run ./cmd/css-visual-diff run \
  --config examples/pyxis-public-shows.yaml \
  --modes capture,cssdiff,matched-styles,pixeldiff,html-report

The report is written to:

<output.dir>/index.html
Prototype-only inspection

The current config shape still requires both original and react targets because css-visual-diff is a comparator. To inspect only a prototype/export target, point both targets at the same URL and use the same prepare hook, then run only capture and report modes:

GOWORK=off go run ./cmd/css-visual-diff run \
  --config examples/pyxis-prototype-only.yaml \
  --modes capture,html-report

In that workflow the second target is just a mirror so the report renderer can reuse its existing two-column UI. Do not interpret pixel diffs from a prototype-only run. Use it to validate the prepared DOM, PNGs, prepared HTML, inspect JSON, and capture validation before comparing against Storybook.

Validation is intentionally layered: inspect DOM text/selectors first, then PNG structure and color-strip statistics. Use human visual review or understand_image for semantic questions such as cutoff/footer visibility; OCR should be a later fallback, not the first validation tool.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL