UX and accessibility review for web apps, in two places: statically in your editor, and against real user flows on every pull request.
The static half is a CLI that parses your JSX and Tailwind classes and reports missing alt text, unlabelled inputs, invisible focus states, inconsistent spacing and colour, and missing loading, empty, and error states. It needs no network and no account.
The flow half runs your app in Playwright, walks a named flow (sign-up, checkout, whatever you define), and analyses each step three ways: axe-core for accessibility violations, a vision model for Nielsen heuristic issues on the captured screenshots, and a rules pass for friction — step counts, slow transitions, redundant steps, overlong forms. Results are merged, scored, and posted back as a pull request comment.
apps/web/ Next.js app: dashboard, marketing site, API routes
packages/cli/ @occhio/cli — the local static scanner
packages/playwright/ @occhio/playwright — flow capture helpers
supabase/migrations/ database schema
docs/ deployment and GitHub App setup
npm install -g @occhio/cli
occhio init
occhio scanocchio scan exits 1 when it finds critical or high-severity issues, so it
drops into CI as a gate without further wiring. It takes --format json for
machine-readable output and --rules missing-alt,focus-visible to narrow the
run to specific rules.
Rules live in packages/cli/src/scanner/rules/,
grouped into accessibility/, consistency/, and ux-patterns/. Each is a
self-contained module against a shared AST visitor, so adding one is a single
file plus an entry in the rules index.
You need Node 20+, a Supabase project, and an Anthropic API key. Stripe, a GitHub App, and a Trigger.dev project are only needed for billing, PR integration, and background crawls respectively — the analysis pipeline runs without them.
npm install
cp .env.example apps/web/.env.local # then fill it in
supabase db push
npm run dev.env.example documents every variable. Setup for the parts with real
prerequisites is written up in docs/: deploying to
Vercel, creating the GitHub
App, and wiring Supabase to GitHub
auth. The running app also serves its own docs
at /docs.
Flow captures are ingested over a single endpoint:
POST /api/v1/analyze
Authorization: Bearer occ_...
The body carries repository_id, pr_number, commit_sha, and a flows[]
array of steps with screenshots and DOM snapshots. Full request and response
shapes are at /docs/api in the running app.
The dashboard, billing, and GitHub PR commenting in apps/web back a hosted
version with monthly review quotas. None of that is required to use the code —
the CLI is standalone, and the analysis pipeline in
apps/web/lib/analysis/ can be driven directly.
npm run dev
npm run build
npm run lint
npm run type-checkMIT — see LICENSE.