| name | one-shot-ui |
| description | Extract UI designs from screenshots and iteratively refine implementations to match a reference image pixel-perfectly using comparison and fix suggestions.
|
You have access to the one-shot-ui CLI tool. Use it to analyze reference
screenshots and compare your implementations against them.
When to Use
- The user provides a screenshot or mockup and asks you to build it
- The user wants to compare their implementation against a design
- The user wants to iteratively refine a UI to match a reference
- The user wants a different UI that copies the style/aesthetic of a
screenshot (same design language, different content) — see "Style transfer" below
Workflow
Step 1: Extract the design
Run one-shot-ui extract <reference.png> --json --overlay to get:
- Layout nodes with positions, sizes, colors, gradients, shadows, border radii
- Text blocks with content, font size, weight, color, and
monospace
- Design tokens (spacing scale, color palette, radius scale)
- Component clusters (repeated visual patterns)
- Implementation plan (suggested CSS strategy: grid/flex/absolute)
Pass --dpr for Retina/Mac screenshots. A 2x screenshot reports every pixel at
double its CSS value (a 35px heading measures 70px), which throws off fonts, spacing
and sizing. If you know the screenshot is from a Retina/Mac display, pass --dpr 2 so
sizes and bounds come back in CSS pixels. Otherwise check the report's scale block:
units: "css-px" means values are normalized; a scale.scaleHint means it might be
Retina and you should re-run with --dpr 2. Build to the reported CSS pixels.
Step 2: Build the UI
Use the extracted data to write the implementation directly. Set exact:
- Colors (hex values from extraction)
- Spacing (px values from spacing measurements)
- Typography (font sizes, weights from text blocks)
- Border radii, shadows, gradients (from style extraction)
Build to the rulers block — this is the single biggest lever for sizing/spacing.
extract reports deterministic pixel-projection "rulers" so you don't have to eyeball or
hand-measure geometry:
rulers.bands — background-zone heights down the page (e.g. a top header/nav bar's exact
height in CSS px, with its background hex). Set those container heights/paddings exactly.
rulers.columns — content column left edges and widths (sidebar width, main column,
card columns). Drive your container widths / grid tracks from these.
rulers.gutters — the exact gaps between columns. Use them for column-gap, margins, padding.
These are CSS px (DPR already applied). Matching them up front avoids the slow "off by ~16px,
shifted right" iteration loop.
Icons: do not hand-draw glyphs. Reproducing icons as inline SVG paths or CSS shapes
almost never pixel-matches and is the most common residual diff. Instead use an icon library:
- Match the icon set the user's codebase already uses if there is one.
- Otherwise default to lucide (
lucide-react, or the framework-appropriate package).
- For a GitHub-style UI specifically, GitHub uses Octicons (
@primer/octicons).
Pick the closest-matching icon by name and size it to the reference (commonly 16–20px).
Step 3: Compare
Capture your build at the reference's exact scale, then diff:
one-shot-ui capture --file ./index.html --match-reference reference.png --output impl.png
one-shot-ui compare reference.png impl.png --json --heatmap heatmap.png
--match-reference now matches BOTH the viewport and the device scale: a 2x Retina reference
is captured at CSS dimensions @ 2x so the pixel sizes line up and no resize/crop is needed.
If the reference is a Retina/Mac screenshot and auto-detection isn't confident, pass
--reference-dpr 2 (the capture prints its detected DPR + a hint when unsure).
For sizing/spacing, read spacing[] (or run compare --spacing). Every compare
reports a spacing[] array of deterministic, directly-CSS-able deltas — BAND_HEIGHT_DELTA
(a bar/section is N px too tall/short), EDGE_X_DELTA (a column's left edge is N px off),
GUTTER_DELTA (a gap is N px too wide/narrow) — each with a concrete fix like
"Reduce its height/padding by 11px." These are the highest-trust fixes; apply them first.
one-shot-ui compare reference.png impl.png --spacing prints just this list.
Read the heatmap to see where differences are. The JSON report includes:
summary.verdict — { status: "converged" | "not-converged", reasons[], completeness }.
This is your stop signal, not the pixel %. Keep iterating while
status is not-converged. The pixel mismatchRatio is dominated by background and
reads as "almost done" even when most elements are missing — do not trust it alone.
mismatchRatio — overall pixel difference (0.0 = perfect match)
issues[] — categorized problems (COLOR_MISMATCH, SPACING_MISMATCH, etc.)
topEditCandidates[] — ranked list of what to fix first
The --summary flag prints one line that leads with VERDICT: CONVERGED / NOT CONVERGED
and the reasons (e.g. missing structural coverage, low hierarchy, a region holding most
of the mismatch). Address the reasons, re-compare, and only stop once it reports
CONVERGED.
Step 4: Converge — pixel-verified CSS optimization (the key step)
Once the structure is in place, run:
one-shot-ui converge <reference.png> --impl ./index.html --json
This is the step that actually closes the last mile. It loads your build in a
controlled browser and runs a closed-loop search: candidate CSS fixes (sizes,
paddings, gaps, margins, exact background/text colors sampled from the
reference pixels, font sizes/weights, border radii) are trialed one at a time,
and ONLY changes that measurably reduce true pixel mismatch are kept. The
output is a verified patch (one-shot-converge/patch.css) — every
declaration in it is already proven against your actual build, with the pixel
gain annotated.
What to do with the result:
- Fold the patch values into your source styles (drop the
!importants).
- If
missingStructure[] is non-empty, those reference regions have no
element in your build — converge never invents elements. Add them, then
re-run converge.
- Repeat until
verdict: "pixel-converged". A css-exhausted verdict means
no CSS change improves the pixels further — the residual is missing
structure, content differences, or rendering noise; it is honest, not stuck.
Why prefer this over manually applying suggest-fixes: suggestions are
one-shot estimates from pixels and can be wrong; converge's fixes cannot be —
each one was measured against the reference before it was kept.
Fallbacks / quick passes
one-shot-ui suggest-fixes <reference.png> impl.png --json — fast one-shot
estimates when you only want hints, not verified fixes.
one-shot-ui run <reference.png> --impl ./index.html --output ./passes —
the older extract→capture→compare→fix loop with per-pass artifacts.
Watch-Mode Server (recommended for iterative work)
Start a local DOM-aware server that watches your HTML file:
one-shot-ui serve --ref <reference.png> --impl ./index.html --port 7777
Query endpoints over HTTP while you edit:
GET /reference — the reference brief (colors, text with bounds, regions).
GET /element?selector=<css> — pass any CSS selector for an element in YOUR
HTML; returns myComputed (real rendered styles), reference (the matched
reference region at the same bounds), and diffs[] (concrete CSS suggestions
that reference YOUR selector, not fabricated names).
POST /apply-temp {"selector": "...", "css": "..."} — trials a candidate
CSS change, scores before/after, returns signed globalDelta/scopedDelta
and a verdict. Use this to pick winning fixes before writing them to disk.
GET /status — current overall mismatchRatio + top-mismatched regions.
Save your HTML file; the server auto-reloads the page in ~120ms. Every edit
yields a measurable mismatch delta — this is the fastest path to convergence.
Style transfer (copy the aesthetic, not the pixels)
Use this when the target is a new screen in the same design language — not a
pixel reproduction of the reference. Don't use compare/converge here; there's
no pixel oracle when the layout differs.
- Extract the system:
one-shot-ui tokens <ref.png> --json → a styleSystem:
palette split into neutrals vs accents, spacing.baseUnit, typography
(size scale + modular ratio), and named radius/elevation scales. Use
--emit shadcn or --emit tailwind for paste-ready variables.
- You own the judgment calls. The tool reports measured facts only — it does
not name semantic color roles (primary/accent/surface/border) or classify
mood. Look at the screenshot and assign those yourself; that's the part a vision
model (you) does better than pixel measurement.
- Build the new UI from those values.
- Verify conformance:
one-shot-ui style-check <ref.png> <your-build> --json.
The new build should be an http(s) URL or HTML file (measured from real computed
CSS — exact) or a screenshot (lossy fallback). Output: per-dimension verdicts.
palette/spacing/typography drive conforms; radius/elevation are
advisory when the reference is a screenshot (a raster can't reliably ground
them). Fix the drifts it reports and re-run until conforms: true.
Output Parsing
All commands support --json. Always use it. Key schemas:
- ExtractReport:
{ image, colors, nodes[], textBlocks[], spacing, components, layoutStrategy, tokens, plan }
- CompareReport:
{ mismatchRatio, pixelDiffCount, issues[], topEditCandidates[], heatmapPath }
Important Notes
- macOS screenshot paths. If the user takes a screenshot and drags the floating
thumbnail in, the path may point at a temporary location (e.g.
…/TemporaryItems/NSIRD_screencaptureui_*/Screenshot ….png) that macOS moves to
~/Desktop once the thumbnail dismisses. If a command reports the file is a moved
macOS screenshot, ask the user for the saved path (usually ~/Desktop/Screenshot ….png)
or have them save it into the project first.
- Reference scale. Pass
--dpr 2 for Retina/Mac screenshots (see Step 1). Sizes and
bounds in the compact report are then CSS pixels — build to those directly.
- Typography.
extract measures font size, weight, and monospace-vs-proportional
from pixels reliably; serif-vs-sans and the exact typeface are best-guess candidates
(fontFamilyCandidates) — confirm the typeface against the screenshot. See
typographyNote in the report.
- Chromium must be installed:
npx playwright install chromium
- Use
--no-ocr to skip OCR if text extraction isn't needed (faster)
- Use
--fine for UIs with small details (icons, small buttons)
- Use
--overlay when you plan to view the reference image yourself — it adds
labeled bounding boxes for cross-referencing