CLI · 12 commands
CLI reference
void is a Bun CLI. It audits a running website in real Chromium on a throttled mobile and a desktop profile, lints source without a browser, extracts design systems from sites you admire, and explains every rule it can report.
$ bun packages/cli/src/index.ts audit http://localhost:3000$ cd packages/cli && bun link && void audit http://localhost:3000Commands
12 commands
- void audit [url]
- Run every page category: perf, smooth, a11y, seo, geo, design, plus lint when a source directory is configured.
- exit 0 · 1 · 2 · 3 · 4
- void perf [url]
- Core Web Vitals and bytes from web-vitals and resource timing, median of --runs, optional Lighthouse.
- exit 0 · 1 · 2 · 3 · 4
- void smooth [url]
- Scripted scroll and interactions: dropped frames, long animation frames, INP, shifts after input, reduced motion, idle rAF, WebGL contexts.
- exit 0 · 1 · 2 · 3 · 4
- void a11y [url]
- axe-core WCAG 2.2 AA, keyboard focus visibility, target size on mobile, 320px reflow.
- exit 0 · 1 · 2 · 3 · 4
- void seo [url]
- Status codes, head placement per user agent, canonical, titles, h1, links, Open Graph, icons, robots.txt, sitemap.
- exit 0 · 1 · 2 · 3 · 4
- void geo [url]
- Raw HTML versus rendered content for AI crawlers, JSON-LD, AI robots policy, llms.txt, Markdown mirrors.
- exit 0 · 1 · 2 · 3 · 4
- void design [url]
- Fonts, type scale, line length, palette and accent sprawl, spacing grid, radius sprawl, generated-UI tells, overflow.
- exit 0 · 1 · 2 · 3 · 4
- void lint [dir]
- Static source checks, no browser. Defaults to the config's src, else the current directory.
- exit 0 · 1 · 2
- void extract <url>
- Measure a site's design DNA and write dna.json, brief.md and direction.css.
- exit 0 · 2 · 4
- void rules [id|category]
- List all rules, a category or prefix, or explain one rule: why, fix, example, refs.
- exit 0 · 2 unknown id
- void report <report.json>
- Re-render a saved JSON report as pretty, md or json.
- exit 0 if it passed, else 1
- void doctor
- Check Bun, Chromium launch, playwright-core, axe-core, web-vitals, optional Lighthouse and the config file.
- exit 0 · 2 · 4
Audit flags
audit, perf, smooth, a11y, seo, geo, design
- --only
- perf,smooth,a11y,seo,geo,design,lint
- Categories to run. The shorthand commands set this.
- --profile
- mobile | desktop | both
- Default both. Mobile is 412×823 at 2.625x with 4× CPU and 150 ms RTT; desktop is 1350×940 unthrottled.
- --routes
- /,/pricing
- Paths resolved against the URL. Default: the URL's own path.
- --runs
- 1–20 (3)
- Perf runs per profile; the median is reported.
- --start
- "bun run start -p 3100"
- Start a server, wait for a 2xx on readyPath, audit, stop the process group. Requires --port.
- --port
- 1–65535
- Port of the started server; the URL becomes http://localhost:<port>.
- --site-url
- https://example.com
- Production origin, so canonical and og:url checks pass while auditing localhost.
- --src
- ./src
- Also lint this directory as part of the audit.
- --kind
- marketing | app
- Selects the default budget set (JS 170 KB vs 300 KB).
- --lighthouse
- boolean
- Also run Lighthouse 13 (optional peer dependency).
- --screenshots
- dir
- Save fold and full-page screenshots.
- --format, -f
- pretty | md | json
- md is compact and grouped by rule, for agents.
- --json
- boolean
- JSON only on stdout; progress goes to stderr.
- --out, -o
- report.json | report.md
- Also write the report to a file; format from the extension.
- --fail-on
- error | warn | info | never
- Exit 1 threshold, default error. Budgets also fail unless never.
- --max-per-rule
- number
- Occurrences listed per rule (md default 5).
- --config, -c
- path
- Default: void.config.ts, .mts, .js or .mjs in the current directory.
- --quiet, -q
- boolean
- No progress output.
- --no-color
- boolean
- Plain output. NO_COLOR is honoured too.
Exit codes
stable across versions
- 0Pass: no findings at or above --fail-on and every budget holds.
- 1Findings at or above --fail-on, or a budget exceeded.
- 2Usage or config error: a bad flag, an invalid void.config.ts, an unknown rule id.
- 3Target unreachable, or the --start server exited or timed out. The last server log lines are printed.
- 4Chromium failed to launch. Run void doctor, install Chromium or set VOID_CHROMIUM.
Output
real output, void 0.1.0
A terminal gets the pretty report: scores, then findings grouped by rule with the file and line, a one-line fix and the docs link. Agents should pass --format md, which groups the same data compactly, or --json to parse it.
$ void lint src void 0.1.0 62 ms Source lint 23 ━━━━━━━──────────────────────── ✖ 2 ▲ 9 ● 2 SOURCE LINT 23 ──────────────────────── ✖ Root layout metadata has no `metadataBase` lint/missing-metadata-base Root layout exports no metadata at all (no `metadataBase`, no default title/description). app/layout.tsx:1:1 → Set `metadataBase: new URL(site.url)` in the root layout, with the production origin. https://void.parthkapoor.me/rules/lint/missing-metadata-base ✖ `utility-[--var]` is Tailwind v3 syntax; v4 needs `utility-(--var)` lint/tw-v3-arbitrary-var `duration-[--duration-fast]` is Tailwind v3 syntax; v4 emits the literal `--duration-fast` as… app/page.tsx:9:43 → Replace `duration-[--duration-fast]` with `duration-(--duration-fast)`. https://void.parthkapoor.me/rules/lint/tw-v3-arbitrary-var ▲ Google Fonts loaded via `<link>` / `@import` lint/google-fonts-link <link> to https://fonts.googleapis.com/css2?family=Inter is render-blocking and adds a third-… app/layout.tsx:3:33 → Use `next/font/google` (self-hosted at build time, with metric-matched fallbacks) or self-h… https://void.parthkapoor.me/rules/lint/google-fonts-link … 10 more findings FAIL ✖ 2 errors ▲ 9 warnings ● 2 info fail-on error Details: void rules <id> · Agent-friendly: --format md$ void rules lint/tw-v3-arbitrary-var `utility-[--var]` is Tailwind v3 syntax; v4 needs `utility-(--var)` lint/tw-v3-arbitrary-var ✖ error Source lint Why In v4 square brackets are literal arbitrary values, so `duration-[--duration-fast]` emits `transition-duration: --duration-fast` — invalid CSS that the browser drops. The transition, color or size silently falls back to the default. Fix Replace `[--name]` with `(--name)` (parentheses are v4's CSS-variable shorthand), or write `[var(--name)]`. Example ```tsx // before (v3) <div className="duration-[--duration-fast] bg-[--brand]" /> // after (v4) <div className="duration-(--duration-fast) bg-(--brand)" /> ``` Docs https://void.parthkapoor.me/rules/lint/tw-v3-arbitrary-var https://tailwindcss.com/docs/upgrade-guide#variables-in-arbitrary-values https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-valuesJSON report
The shape of --json, abridged, with illustrative values. Every finding links to its rule page, for example perf/lcp-image-lazy.
{
"schemaVersion": 1,
"tool": { "name": "void", "version": "0.1.0" },
"scores": { "perf": 97, "smooth": 100, "a11y": 100, "seo": 100, "geo": 96, "design": 94 },
"overall": 98,
"metrics": [{ "name": "lcp", "value": 1180, "unit": "ms", "profile": "mobile", "budget": 2500 }],
"findings": [{
"id": "perf/lcp-image-lazy",
"category": "perf",
"severity": "error",
"message": "LCP image /hero.avif has loading=lazy",
"source": "web-vitals",
"target": { "url": "http://localhost:3100/", "profile": "mobile", "selector": "main img" },
"fix": { "hint": "Remove loading=\"lazy\" from the LCP image." },
"docs": "https://void.parthkapoor.me/rules/perf/lcp-image-lazy"
}],
"summary": { "errors": 1, "warnings": 0, "infos": 2, "pass": false }
}void.config.ts
auto-detected in the working directory
- urlstring
- Base URL when none is passed.
- routesstring[]
- Paths to audit.
- start{ command, port, cwd?, readyPath?, timeoutMs? }
- Server to spawn. readyPath defaults to /, timeout to 120 s.
- srcstring
- Source directory for lint; also added to audit.
- profiles("mobile" | "desktop")[]
- Default both.
- runs1–20
- Perf runs per profile, default 3.
- kind"marketing" | "app"
- Default budget set.
- budgets{ mobile?: Budgets, desktop?: Budgets }
- lcp, inp, tbt, ttfb (ms), cls, jsKb, cssKb, fontKb, imageKb, htmlKb, requests, droppedFramePct. Merged over the defaults.
- rulesRecord<ruleId, "error" | "warn" | "info" | "off">
- Severity overrides per rule id.
- failOn"error" | "warn" | "info" | "never"
- Default error.
- lighthouseboolean
- Default false.
- onlyCategory[]
- Default: all page categories.
/** @type {import('@void/cli/config').VoidConfig} */
const config = {
kind: "marketing",
src: "src",
start: { command: "bun run start -p 3100", port: 3100, readyPath: "/" },
routes: ["/", "/pricing", "/this-page-does-not-exist"],
profiles: ["mobile", "desktop"],
runs: 3,
budgets: { mobile: { lcp: 2000, jsKb: 150 } },
rules: { "geo/answer-first": "off" },
failOn: "error",
};
export default config;Default budgets
override per key in void.config.ts
| Profile | LCP | CLS | INP | TBT | TTFB | JS | CSS | Fonts | HTML |
|---|---|---|---|---|---|---|---|---|---|
| marketing · mobile | 2.5 s | 0.1 | 200 ms | 200 ms | 800 ms | 170 KB | 25 KB | 120 KB | 30 KB |
| marketing · desktop | 1.5 s | 0.1 | 200 ms | 200 ms | 800 ms | 170 KB | 25 KB | 120 KB | 30 KB |
| app · mobile | 2.5 s | 0.1 | 200 ms | 300 ms | 800 ms | 300 KB | 40 KB | 120 KB | 60 KB |
| app · desktop | 1.5 s | 0.1 | 200 ms | 200 ms | 800 ms | 300 KB | 40 KB | 120 KB | 60 KB |
The full reference agents load is audit / commands.