Skip to content

Configuration ​

visualice.config.ts is the worked example; every field is optional and falls back to the defaults in src/shared/config.ts. JSON configs are accepted too, and config.schema.json gives them validation and editor autocomplete:

jsonc
{
  // what `visualice init` scaffolds; "./config.schema.json" in this repo itself
  "$schema": "https://unpkg.com/visualice@0.1.1/config.schema.json",
  "target": "https://your.site",
  "scope": { "exclude": ["/admin/**"] },
}

Options worth knowing:

OptionDefaultWhat it does
politeness.respectRobotstrueObeys robots.txt Allow/Disallow and Crawl-delay while discovering routes
politeness.retries1Extra attempts for a failed navigation/capture before recording a failure
politeness.reuseNavigationtrueSkip the reload when the next state's recipe just extends what the page already shows
scope.samples{}At most N pages per path glob ({ "/product/*": 3 }): a few pages of a template stand for the rest, whatever their ids
scope.hashRoutes"auto"auto treats #/about-style fragments as routes and plain anchors as anchors; never/always force it
discovery.interactions.autotrueProbe pages for UI states; false captures each page's default render only
discovery.interactions.nestDepth11 = states one interaction away, >1 = also probe inside opened states (expensive)
discovery.interactions.maxPerPage12Hard cap on states per route, whatever the nesting depth
discovery.interactions.hovertrueAlso probe hover-revealed states (dropdown navs)
discovery.interactions.siteWideOncetrueA state found identically on several pages (header dropdown, chat widget) is kept on the first page only
discovery.mobileStatestrueSecond probe pass at the first mobile device's viewport (finds hamburger navs)
testIdAttribute"data-testid"The attribute the site's test ids live in (data-test, data-cy, …); testId targets and discovery use it
devices[].colorScheme"light"prefers-color-scheme per device — add a dark device for a dark-mode catalog
devices[].locale / timezoneIden-US / UTCPinned so Intl/date rendering is machine-independent
devices[].browser"chromium"firefox or webkit captures that device in another engine (npx playwright install <name> first)
components[]{ name, selector, routes } — screenshot an element on its own, on every route that has it
checks.accessibilityfalseRun axe-core on every captured state; results land in the report, the sidecars and the viewer
stabilize.freezeTime / seedRandomunsetPin Date / Math.random in the page so reruns are byte-identical
stabilize.maskSelectors[]Paint opaque boxes over volatile regions (ads, clocks, carousels)
stabilize.waitForSelectors / …TimeoutMs[] / 5000Selectors that must be visible before a shot; they are waited on together and share the budget
screenshot.alsoViewportfalseAlso write an above-the-fold crop next to each full-page shot
screenshot.fullPage"screens"screens: one screen at a time, as a user scrolls, so fixed bars and backdrops show truthfully; single: one tall render; false: the first screen only
screenshot.maxHeight25000Longest page captured, in CSS px, in either full-page mode; a longer one is cut there and flagged clamped; 0 disables (watch memory)
screenshot.thumbnail.zoomWidth1024Width of the in-between image the viewer shows while zooming in; 0 disables
http.ignoreHTTPSErrorsfalseAccept self-signed TLS (staging environments)
http.httpCredentials / extraHTTPHeadersunsetBasic-auth / preview-token headers; values may use ${ENV_VAR}
routesFilevisualice.routes.mdWhere the editable site index lives; false disables it entirely

More than page screenshots ​

Other browsers. Give a device browser: "firefox" or "webkit" and it is captured in that engine, beside the Chromium ones, as another column in the catalog. Discovery still runs in Chromium; the recipes it records are replayed in each engine.

jsonc
"devices": [
  { "name": "desktop", "width": 1440, "height": 900, "dpr": 2 },
  { "name": "safari", "width": 1440, "height": 900, "dpr": 2, "browser": "webkit" }
]

Components. components photographs an element on its own — a header, a pricing card — on every route that has it, as a state of kind component. A route without the element is skipped rather than failed. A hand-written state can carry element too, with a recipe, for a component that has to be opened first.

jsonc
"components": [
  { "name": "Header", "selector": "header" },
  { "name": "Pricing card", "selector": ".pricing-card", "routes": ["/pricing/**"] }
]

Accessibility. checks.accessibility: true runs axe-core against every state while it is on screen — including inside the opened modals and menus a static scan never reaches. The crawl report gets an Accessibility section (rules, impact, how many shots), each shot's sidecar lists the elements, --json carries the roll-up, and the viewer badges the tiles and lists the findings in the inspector.

MIT licensed.