vernier
v0.1.2
Published
Local UI feedback layer for turning visual bugs into agent-readable fix packets.
Maintainers
Readme
Vernier
Vernier is a local UI feedback layer for turning visual bugs into measured, agent-readable fix packets.
It gives you a browser overlay for pointing at layout problems, records the DOM/layout evidence an agent needs, stores everything locally, and exports packets for tools like Codex and Claude.

Why
Most UI bug reports lose the details that matter: the exact element, viewport, selector, computed styles, screenshot, source hint, and verification path.
Vernier captures those details while you are looking at the app, then turns them into a reproducible local artifact:
- measured element bounds, computed styles, text metrics, layout context, and selector confidence
- screenshots with automatic password redaction and optional manual redaction
- issue notes, status, route, viewport, and source hints
- Markdown, JSON, zip, GitHub issue bodies, and agent handoff packets
- follow-up verification commands that can remeasure the issue after a fix
Install
Requires Node.js 20 or newer.
npm i -D vernier
npx vernier --versionQuick Start: Any Localhost App
Run your app on a local port, then attach Vernier:
npx vernier attachattach scans common dev ports, starts a local proxy for the best match, and opens it in your browser. Add an explicit target when needed:
npx vernier attach --target http://localhost:3000
npx vernier --target http://localhost:3000
npx vernier start --target http://localhost:3000 --port 3333Open the proxied URL, then press:
Ctrl+Shift+FChoose Measure, click the UI element that looks wrong, add a note, and export.

Quick Start: Vite Plugin
Add Vernier to your Vite config:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { vernier } from "vernier";
export default defineConfig({
plugins: [react(), vernier()]
});Start your Vite dev server, open the app, then press Ctrl+Shift+F.
Quick Start: Script Tag
If you can edit the app HTML directly, run the standalone Vernier asset server and paste the snippet before </body>:
npx vernier serve --port 3333
npx vernier snippet --port 3333This works for plain HTML, backend-rendered pages, Storybook preview HTML, and frameworks where a Vite plugin is not available.
Capture Workflow
- Press
Ctrl+Shift+F. - Choose a mode:
- Measure: hover elements, click one element for a single measurement, click a second element for a delta.
- Pen: draw a freehand annotation.
- Box: drag a rectangular annotation.
- Redact: drag a mask over sensitive screenshot regions.
- Add a short note describing what should change.
- Click Add issue.
- Repeat for every visual issue you want to hand off.
- Click Export.
Vernier writes local artifacts under:
.ui-feedback/latest/session.md
.ui-feedback/latest/session.json
.ui-feedback/latest/screenshots.json
.ui-feedback/latest/screenshots/Use the CLI after export:
npx vernier issues
npx vernier show <issue-id>
npx vernier copy <issue-id> --format packet
npx vernier send <issue-id> --to codex --template strict --print
npx vernier verify <issue-id> --target http://localhost:3000 --compare
README Screenshot Workflow
This is the recommended way to test Vernier and regenerate README media. It produces static PNG screenshots, not an MP4.
npm install
npm run verify:m0
npx playwright install chromium
npm run assets:launchThe asset script:
- builds Vernier
- starts the local React/Vite example app
- starts the Vernier proxy
- opens the overlay with Playwright
- captures a measurement screenshot
- writes a local example session
- captures replay and CLI output screenshots
Review these files before committing:
docs/assets/vernier-overlay-measurement.png
docs/assets/vernier-issue-list.png
docs/assets/vernier-generated-packet.png
docs/assets/vernier-doctor.png
docs/assets/vernier-send.png
docs/assets/vernier-verify.pngFor a manual smoke test, run the same flow yourself:
npm run build
npm run dev:example -- --host 127.0.0.1 --port 5173 --strictPort
npx vernier attach --target http://127.0.0.1:5173Then open the Vernier proxy, press Ctrl+Shift+F, click the revenue card in the example app, add a note, export, and run:
npx vernier issues
npx vernier show <issue-id>
npx vernier verify <issue-id> --target http://127.0.0.1:5173Configuration
Optional defaults can live in vernier.config.json:
{
"target": "http://localhost:5173",
"port": "auto",
"detectPorts": [5173, 3000, 6006],
"verification": {
"bboxTolerancePx": 2
},
"overlay": {
"captureFullPage": false,
"captureStrategy": "modern-screenshot"
},
"agents": {
"default": "codex"
}
}Flags win over environment variables, environment variables win over config, and config wins over built-in defaults. Supported environment defaults are VERNIER_TARGET, VERNIER_PORT, VERNIER_PORTS, VERNIER_AGENT, and VERNIER_DEBUG=1.
Supported overlay screenshot strategies are html2canvas and modern-screenshot. html2canvas remains the default; use modern-screenshot when you want the newer DOM-to-canvas renderer.
Privacy
Vernier is local-first:
- screenshots, session JSON, verification artifacts, and replay assets are written to local disk
- Vernier does not upload screenshots or session data
.ui-feedback/should stay gitignoredmetadata.jsonrecordslocalOnly: trueandnetworkUploads: falsefor exported sessions- the session write endpoint validates payload shape, caps body/screenshot sizes, accepts only safe screenshot filenames, confines writes to the project output directory, and rejects non-local cross-site browser origins
Screenshots automatically mask password inputs and elements marked with data-vernier-redact. Vernier shows a one-time local screenshot warning before the first export. Set overlay.captureFullPage to false when you want the exported overview screenshot cropped to the current viewport instead of the whole page.
Source Hints
Element measurements include computed styles, authored CSS hints, layout context, text metrics, stacking context, utility-like class hints, and nearby CSS custom properties that match captured values. This helps agents reuse existing classes and design tokens instead of inventing one-off values.
For non-React apps or compiled frameworks, add data-vernier-source="src/components/Button.tsx:37" to give Vernier an exact source hint. You can also add data-vernier-component="Button" and data-vernier-owner-chain="Page > Card > Button" when a file/line is not available.
Proxy Notes
The proxy injects Vernier into HTML responses and forwards everything else to your target app, including redirects, cookies, SSE streams, and WebSocket upgrades used by HMR.
Known limitation: HTML responses are buffered before Vernier injects the overlay. Server-Sent Events are streamed through, but streaming HTML responses such as React renderToPipeableStream will not stream progressively through the Vernier proxy.
Your target app must already be running. If the target app is down, Vernier keeps running and shows a 502 page explaining that the target refused the connection.
Vernier mounts its browser chrome in a Shadow DOM host so app-level CSS resets and component styles do not distort the overlay, and Vernier styles do not leak back into your app.
Development
npm install
npm test
npm run lint
npm run verify:m0
npm run test:e2e
npm run test:proxy
npm run dev:example
npm run dev:proxy
npm run proxy:3000Release notes and npm publishing steps live in docs/release-process.md.
Command Reference
Common local app commands:
vernier attach
vernier attach --target http://localhost:3000
vernier detect
vernier detect --ports 5173,3000,6006
vernier detect --json
vernier serve --port 3333
vernier snippet --port 3333Issue and session commands:
vernier issues
vernier issues --todo
vernier issues --fixed
vernier issues --json
vernier status
vernier show <issue-id>
vernier note <issue-id> "Button should align with card title"
vernier rename-session "pricing mobile pass"
vernier mark <issue-id> fixed
vernier mark <issue-id> todo
vernier latest
vernier openHandoff commands:
vernier copy <issue-id>
vernier copy <issue-id> --format packet
vernier send --to codex --template codex
vernier send <issue-id> --to codex --template strict
vernier send all --to claude --all
vernier github body <issue-id>
vernier github create all --label ui-feedback --dry-runVerification and visual capture commands:
vernier verify <issue-id>
vernier verify <issue-id> --target http://localhost:3000 --open
vernier verify <issue-id> --target http://localhost:3000 --compare
vernier verify <issue-id> --target http://localhost:3000 --compare --viewports mobile,tablet,desktop
vernier capture --target http://localhost:3000 --routes /,/pricing --viewports mobile,desktop
vernier diff .ui-feedback/captures/capture-a .ui-feedback/captures/capture-b
vernier replay latest
vernier storybook --url http://localhost:6006 --stories button--primary --viewports mobile,desktopMaintenance and agent-loop commands:
vernier doctor
vernier clean --keep 20 --dry-run
vernier audit a11y
vernier audit layout
vernier mcp
vernier plan <issue-id>
vernier fix-loop <issue-id> --to codex --target http://localhost:3000
vernier export --format zip
vernier export --format json --out latest-session.json
vernier import .ui-feedback/exports/latest-session.zip
vernier import path/to/session-directoryvernier issues prints short stable IDs like i-8f3a12 plus a todo or fixed status, so you do not have to rely on fragile list positions when multiple issues are waiting. vernier status gives a compact latest-session count and next todo item.
Add --json to vernier issues, vernier status, or vernier detect when you want scriptable output for agents, CI, or shell pipelines.
vernier copy <issue-id> --format packet outputs a compact reproduction packet with route, viewport, selector, source hint, screenshot path, note, and verification commands. Use --print to write it to stdout instead of the clipboard.
vernier fix-loop <issue-id> --to codex --target http://localhost:3000 sends a Vernier repair task to the selected agent, waits for it to exit, runs vernier verify --compare, and marks the issue fixed only when the measured result is inside tolerance. Add --print to inspect the task without launching an agent.
vernier send --to codex sends todo issues in the latest session by default. Use --template generic|codex|claude|cursor|aider|strict to tune handoff framing. Use --all when you want fixed issues included too. If the Codex or Claude CLI is not installed, Vernier copies the task to your clipboard so you can paste it into the desktop app.
vernier verify <issue-id> --compare reopens the captured route at the captured viewport, finds the selector, remeasures it, and writes local artifacts under .ui-feedback/sessions/<session>/verification/<issue-id>/. Add --viewports mobile,tablet,desktop or explicit sizes like 390x844,768x1024,1440x900@2 to compare the same issue across responsive breakpoints.
vernier replay latest opens a local read-only viewer for the latest session, including screenshots, structured evidence, statuses, and verification reports.
