I turned a manual, screen-by-screen design review into an engine that logs into any web product, audits every state, and ships developer-ready annotated reports — at ~$0.30 a screen, versus hours of a senior's manual review.
Point it at a product URL with credentials, and it logs in, walks every screen, clicks through to every interactive state, and produces a self-contained report — one clean numbered box per problem.
At Omnis AI I was the solo product designer across five legal-tech products, asked to manually audit our live staging apps screen by screen. That's slow, inconsistent, and doesn't scale — so instead of doing it by hand forever, I built the thing that does it for me. The non-negotiable principle that shaped everything: findings must be grounded in the real DOM, not guessed.
The signal, from being the bottleneck myself: auditing those five products by hand, the issues I consistently missed were the hidden states — modals, error and empty screens I simply forgot to open. That finding, not a spec, is why exhaustive state exploration — clicking every control and auditing each resulting state — became the engine's first-class job rather than a nice-to-have.
The whole project is really a story about a broken workflow and the one I replaced it with.
| Dimension | Before · manual | After · the engine |
|---|---|---|
| Time per screen | ~2–3 hrs of a senior's day | ~3 minutes |
| Cost per screen | ~$400 | ~$0.30 |
| Interactive states | Usually skipped | Every state captured |
| Consistency | Varies by reviewer | Identical, rule-based |
| Coverage record | None | "N elements, 0 skipped" |
| Dev handoff | Static image + Q&A | Selector + copy-paste fix |
| Re-audit after a fix | Full manual redo | One command; shows deltas |



Because it has live access — not just a static screenshot — it:

Each finding carries a severity chip for triage, the category + rule ID so it traces to the exact rule that fired, a plain-English title, a Why → How → Impact reasoning chain that names the exact principle, a copy-paste Fix, a confidence score, and a Copy-as-markdown button. A finding missing any of these is rejected before it reaches the card.

The engine (ux-audit-mcp) does capture + analysis. The service (ux-audit-service) turns it into a multi-product dashboard with CI.
| Layer | Technology |
|---|---|
| runtime | Node + TypeScript, Model Context Protocol (MCP) SDK |
| capture | Playwright — login, full-page screenshots, DOM inventory, state exploration |
| a11y | @axe-core/playwright — the axe engine, live per page |
| ai-vision | OpenAI-compatible SDK → Gemini Flash / OpenRouter (multi-provider + fallback) |
| image | Sharp (annotation), tesseract.js (OCR snapping), pixelmatch (diff) |
| service | Next.js 16, React 19, Tailwind 4 · Prisma 7 + Postgres · BullMQ + Redis · GitHub Actions · Docker |
A deliberate product decision: the same underlying findings are presented six ways so each role gets exactly what it needs.



Anyone can draw a box on a screenshot. The judgment is in making the report trustworthy.
CaseNotes (our own staging app): an early 4-screen audit found 89 issues across 22 interactive states in ~11 minutes for ~$1.20 — 35 deterministic, 45 AI, 22 content, across 1,035 elements; 26 auto-fixed; dedup removed 800+ duplicates. AutoDoc: 87 findings (4 blocker, 26 major, 42 minor, 12 nit, 3 praise). Also proven on public logins — GitHub (29) and Vercel (26).
It designs its own edge states, too: a clean "0 issues found" report when a screen passes, and a login-failure path that falls back to a saved session or scripted login instead of erroring out — the same reality-first standard it audits others against.
Measured API + compute cost. The manual alternative is ~half a senior's day per product — my estimate at contractor rates (~$400), not a billed figure.
Every interactive state clicked & captured — "N elements, 0 skipped."
Every box sits on a real DOM element — never floating near it.
The engine exports a complete audit as a self-contained HTML file and clean Markdown (paste into a PR or ticket), and rolls every screen into one product dashboard. A verbatim excerpt from the Markdown export:
**1. Low contrast on div "Profile": 1.3:1 (need 4.5:1)** - Why: Insufficient text contrast makes content hard to read. - Impact: Users with low vision cannot read this → WCAG non-compliance. - Fix: Increase contrast to at least 4.5:1. - rule: DET-CONTRAST-001 · WCAG 1.4.3 (AA) · confidence: 1 - div.sidebar-footer-profile.expanded


It closes the design → build loop most designers never close — so I learned the things only visible once it's real: the bottleneck was trust, not coverage; a box one pixel off destroys credibility; "acceptance %" is a vanity metric.
It's evidence of judgment, not pixels — problem-framing, explicit trade-offs, a defined success metric, and the versions I killed. It's systems thinking across states, flows, failure modes and six user roles, with craft in service of trust.
The version I killed: an early build analysed static screenshots only. It confidently flagged elements that weren't really there — so I rebuilt it around live DOM access. Every finding now has to point at a real element or it doesn't ship. That single constraint is why the reports are trustworthy, and it's the whole reason "grounded in the real DOM" became the non-negotiable.
What I'd do differently: build the measuring stick first. I spent early weeks adding rules because progress felt fast, then realised the real bottleneck was trust, not coverage. For any AI product, decide how you'll measure "good" before you build it — volume is easy, trust is the hard, valuable part.
Leverage, not just output: the engine became the audit method across all five Omnis products, and one run serves six roles — design, PM, three engineering disciplines and leadership — off a single grounded report. The work compounded past my own hands, which is the point.