
Image To Ui
- 4 installs
- Updated June 12, 2026
- ceorkm/image-to-ui
Helps with ai & agent building tasks.
About
image-to-ui is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- image-to-ui
- AI & Agent Building
- AI-coding skill
Image To Ui by the numbers
- 4 all-time installs (skills.sh)
- Ranked #13,372 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/ceorkm/image-to-ui --skill image-to-uiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| Last updated | June 12, 2026 |
| Repository | ceorkm/image-to-ui ↗ |
What it does
Helps with ai & agent building tasks.
Files
Image to UI
Battle-tested across landing pages, dense trading dashboards, mobile apps, macOS apps, and photographic heroes — every build shipped 1:1 AND fully functional. The flow is fixed; the detail lives in references/. Read the reference file for the phase you are in BEFORE doing that phase — they contain the measured numbers, code patterns, and agent briefs that make the difference between "close" and "1:1".
The flow (fixed — do not reorder)
1. MEASURE the image → references/01-measurement.md Scale factor, ink-extent typography, font ID with weight verification, color grammar, photographic techniques. Nothing is built until ANALYSIS.md holds the numbers. 2. BUILD the 1:1 clone — SOLO. You build it yourself; never delegate the build to a background agent. → references/02-build-engines.md for the engine matching the category (dashboard / mobile / landing / mac app / hero) and every layout gotcha. 3. VERIFY — mandatory, automatic. Run your own diff loop until clean, then ALWAYS spawn TWO adversarial verification agents in parallel (pixel-fidelity + product/structure) against the original image. Adjudicate every finding against the artifact (~1 in 8 is false), apply the real ones by hand, re-verify. → references/03-verification.md has the diff tooling and the verbatim agent briefs. 4. PRESENT to the user and STOP. Side-by-side image vs render + the diff numbers. The functional phase does not start until the user approves the clone. The user's eyeball verdict overrides any metric — if they say "done", stop verifying; if they say something is off, the metric missed it — find what they saw. 5. MAKE IT FULLY FUNCTIONAL. Freeze the regression baseline first; the approved pixels are sacred. Extract the design system from the reference and extrapolate every unseen page/view with the recombination method — this is how ONE screenshot becomes a complete coherent app. → references/04-functional.md (the core method — read it in full). 6. VERIFY the functional build the same way: assertion suite + regression gate, then two adversarial reviewers (one functional, one design-consistency vs the approved screen as source of truth). Apply, re-gate, deliver.
Non-negotiable standards (apply to every phase)
- UI must FILL the browser window. Never letterboxed, never a floating panel on a backdrop.
If the reference is a presentation shot (app on a decorative canvas), clone the PANEL CONTENT full-bleed and diff against the panel crop, not the canvas.
- Real Unsplash photos only; human faces via
?fit=facearea&facepad=2.2-2.5. Every image at
>=2x its largest rendered size, q>=85. Encode MOOD adjectives in image searches.
- Icons: Phosphor web font (Lucide acceptable). NEVER emojis, NEVER hand-drawn icon SVGs.
Charts/sparklines traced from the reference as SVG are data, not icons — those are fine.
- Density: no big empty cards, no dead space, ever.
- Reference fidelity beats copy sense: transcribe the reference's text verbatim, typos
included (offer corrections only after approval).
- Every control does something real — no dead taps. A control that lies is worse than no
control. Honest copy for demo features ("canned replies, not live AI").
- Zero console errors, zero failed requests — verified, not assumed.
- Single self-contained index.html, no build step, served via
python3 -m http.server.
Output layout (per project)
<project>/index.html single self-contained build
<project>/ANALYSIS.md every measurement, written incrementally DURING phase 1-2
<project>/PROGRESS.md phase state + exact resume instructions, updated per section
<project>/measure/ crops, renders, diffs, baselines, verification scriptsPROGRESS.md discipline is not optional: sessions die without warning; a successor (or you, after a crash) must be able to resume from disk alone. THE DISK IS THE TRUTH.
.DS_Store
image-to-ui
An agent skill that turns any UI reference image into a pixel-faithful 1:1 clone, and then into a fully functional app — landing pages, dashboards, mobile apps, macOS apps, photographic heroes.
Drop a screenshot, say "build this", and the skill runs a fixed pipeline:
1. Measure — the image is treated as a design specification, not an inspiration: scale-factor detection, ink-extent typography, canvas font identification with weight verification, color budgets, full component inventory. Nothing is built until the numbers exist. 2. Build (solo) — one self-contained index.html, no build step, wrapped in an elastic engine so the UI fills any window (dashboards stretch their middle columns; mobile apps live in one interactive phone that becomes the real screen on an actual device). 3. Verify (adversarial, mandatory) — a pixel diff loop against the reference, then two independent review agents: one pixel-fidelity, one try-to-break-it. Every finding is adjudicated against the artifact before it's applied — roughly 1 in 8 plausible-sounding review findings is false. 4. Approval gate — side-by-side presentation. The functional phase doesn't start until a human approves the clone. The human eyeball overrides every metric. 5. Make it real — the design system extracted from the one known screen (layout grammar, color budget per screen, type scale, hidden component library, implied behavior contracts) is recombined to build every page the reference only hinted at. Every control works, every flow completes and snapshots its committed state, persistence survives reloads, and a frozen-pixel regression gate keeps the approved screen byte-identical to the very end.
Battle-tested across eleven builds in one run: landing pages, two dense trading dashboards, three mobile apps, a macOS music app, and a photographic hero — every one shipped 1:1 and fully functional.
Install
# Claude Code (or any agent supporting the skills format)
cp -R . ~/.claude/skills/image-to-uiThe skill triggers automatically when you give the agent a UI screenshot/mockup and ask for it to be cloned, recreated, or built.
Structure
SKILL.md the fixed flow + non-negotiable standards (always loaded)
references/
01-measurement.md scale detection, ink typography, font ID, photographic techniques
02-build-engines.md per-category engines (dashboard/mobile/landing/mac/hero) + layout gotchas
03-verification.md diff tooling, the two reviewer-agent briefs, adjudication, regression baselines
04-functional.md design-system extraction & page extrapolation — how one screenshot becomes a whole appSKILL.md stays lean and is loaded with every session; the agent reads the relevant reference file at each phase, so the depth costs nothing until it's needed.
The ideas that carry the method
- Measure, don't eyeball. Font weight is the #1 silent killer — verified by counting
ink pixels in identical boxes, because two fonts can match height while one runs 40% heavier.
- The reference is a rulebook. One screen proves the grid, the color budget, the type
scale, the component set, and the implied interaction contracts. Unseen pages are built by recombination only — nothing the original designer didn't draw — which is why they don't read as AI filler.
- Reviewers are adversaries, and so are their findings. Two agents try to refute every
build; their findings are then verified against the artifact before being applied.
- The approved screen is sacred. Once a human signs off, a frozen-baseline pixel gate
guards it through the entire functional build — zero differing pixels, enforced by script.
- A control that lies is worse than no control. Everything works or it isn't shipped:
exports produce real files, resets really wipe, demo features say so honestly.
Phase 1 — Measurement playbook
Nothing gets built until ANALYSIS.md holds the numbers. You are not forming an impression of the design — you are extracting its rulebook. Write every finding into ANALYSIS.md AS YOU MEASURE, never at the end.
1. Scale factor first
Load the image with PIL. Divide its width by candidate design widths until chrome elements land on whole sensible numbers:
- Web: 1440 / 1512 / 1280 / 1024 / 1600 (and 2x captures of each — a 2048x1536 image is
almost always a @2x shot of 1024x768)
- Mobile: 390 / 393 / 430 logical (iPhone), check the aspect ~0.46
- Dribbble shots: try canvas ÷ 2.8369 (1600x1200 standard) and look for the inset panel —
measure the panel bounds precisely; THE PANEL is your design, the canvas is presentation
- Low-res references (<600px wide): you cannot trust 1px reads. Reconstruct design intent
from platform standards (macOS title bar 28pt, traffic lights 12px at 20px from left, sidebar 220-260; iOS status 44/59, tab bar 49+home) and state every inference in ANALYSIS.md as an inference, not a measurement.
Sanity checks that confirm a scale: toolbar height, gutter widths, sidebar width, card radii all land on integers or clean halves. Fractional garbage = wrong factor.
2. Geometry
Crop and zoom every region (PIL crops into measure/). For each block record: x, y, w, h in logical px, border-radius, gaps/pitches of repeating rows. Use edge detection on rows/columns (horizontal gradient magnitude) for card boundaries; bg-color row profiles (flat-row std<12) for section boundaries on long pages. Measure repeating structures by their PITCH (row pitch, column pitch), not individual items.
3. Color
Multi-point sample every surface (corners + center, never one pixel). Record: page bg, card surfaces, every accent, all text grays, gradients (sample at grid points + note direction). Count the COLOR BUDGET per screen (e.g. "exactly 1 yellow card + 1 light card + N dark") — this ratio is part of the design system and will drive page extrapolation later.
4. Typography — ink extents, never eyes
- Cap height: isolate a glyph run, threshold at fixed luminance, measure ink rows.
font-size ≈ cap / 0.72 (most grotesques); verify per family.
- Ink width of specific strings → identifies the family: render candidates in a canvas
harness (10+ candidates), compare ink-width ratios or IoU of ink masks vs the reference crop. Best-in-class evidence: width/cap ratio per anchor string + per-letter ratios.
- WEIGHT is the #1 silent killer; height alone lies. Verify by counting ink pixels at a fixed
luminance threshold in IDENTICAL boxes (reference vs render). Target ink ratio 1.00-1.06x. Always set -webkit-font-smoothing:antialiased before judging ink.
- Variable-font traps: the
opszaxis renders UI sizes ~12% wider than design tools — pin
font-variation-settings:'opsz' N on body AND button/input (UA font shorthand resets it). Inter wants 'opsz' 28-32 for dashboard micro-type; DM Sans wants 'opsz' 40.
- Display numbers in modern health/fintech designs are often CONDENSED beyond any standard
family: match the measured ink width with transform:scaleX(0.75-0.85) on the number element (height from font-size, width from scaleX), or use a width-axis variable font.
- Micro type (dashboards run 6.8-11px logical): measure isolated glyph caps; a "10px-looking"
band is often a 7.2px font contaminated by neighbors.
- Beware contaminated ink windows: stars, icons, adjacent gray text inside your measurement
box will lie to you. Isolate before measuring; verify "defects" before fixing them.
5. Photographic references
- Scene with baked-in UI (hero shots): inpaint ALL baked UI out of the reference (multigrid
Laplace/harmonic fill for sky, row-interpolation for horizon bands, directional propagation for walls; feather seams) → use the cleaned photo as a base64-inlined bg plate → rebuild every UI element LIVE on top. Frosted pills get real backdrop-filter over the photo.
- Text behind imagery (wordmark behind mountains): bg photo + alpha-cutout fg (sky removed)
+ live text sandwiched between. Top-anchor both images (object-position:50% 0) so cover- crop only eats the bottom and ridge position scales LINEARLY with viewport width; set the wordmark font-size/top in source-image pixels via JS: scale = max(vw/IMG_W, BOX_H/IMG_H); fontPx = F0*scale; topPx = T0*scale. Verify occlusion DEPTH numerically (count near-white ink rows visible vs the reference). If one letter is over-swallowed because your substitute photo's peak is taller there: surgically roll the fg alpha down per-column in that letter's x-range only (np.roll each alpha column by a smoothed raised-cosine delta; preserves the natural jagged silhouette; RGB needs no edit because fg and bg are the same photo). Back up the original first.
- Photo sourcing: Unsplash API requires auth ("Authorization required") — scrape
unsplash.com/s/photos/<query> for /photos/<id> links and resolve image URLs via the https://unsplash.com/photos/<id>/download redirect (curl -sI -w '%{redirect_url}'). Build contact sheets of candidates, pick in context, cache all responses to measure/. References are often AI renders — unfindable; mood-match instead, and encode the MOOD in the query ("bright saturated daylight" vs subject-only — the one section whose query carried the wrong adjective is the one that ships wrong).
6. Charts and data visuals
Extract chart geometry point-by-point from the reference pixels (trace the polyline, read bar heights at each x) and embed as SVG with explicit points. Note halftone/dot textures (pitch, alpha) — recipe: repeating radial-gradient overlays. These surfaces will saturate pixel-diff metrics later; record sampled gradient colors at grid points as the verification target instead.
Phase 2 — Build engines per category
One self-contained index.html, no build step. Pick the engine by what the image shows. Always build SOLO — the build is never delegated to a background agent.
A. Dashboard / desktop web app
Fixed logical canvas (whatever phase 1 measured, e.g. 920x660) wrapped in the elastic full-bleed engine. The app IS the page — no backdrop, no panel float, no letterboxing.
// ZOOM is the density dial: 1.0 = full width-cover. Raise it when the user says
// "too zoomed in", lower toward 1.0 for "too zoomed out". One-line tuning.
const W = 920, H = 660, ZOOM = 1.0;
function fit(){
const vw = innerWidth, vh = innerHeight;
const k = Math.max(vw / (W * ZOOM), vh / H); // width-driven cover
const app = document.getElementById('app');
app.style.width = (vw / k) + 'px';
app.style.height = Math.max(vh / k, H) + 'px';
app.style.transform = 'scale(' + k + ')'; // transform-origin: 0 0
document.body.style.height = (Math.max(vh / k, H) * k) + 'px'; // page scrolls vertically
}html,body{overflow-x:hidden;overflow-y:auto}+::-webkit-scrollbar{width:0}- body/app background = the app's own surface color (never a distinct backdrop tone)
- Elastic anchoring: middle column elements get
width:auto; right:<rail-width+gap>px;
right-rail cards left:auto; right:0; topbar width:auto;right:0 with its right-side controls (search, bells, avatar) converted to left:auto; right:Npx. Left-anchored card internals stay put; right-anchored internals follow automatically when cards widen.
- Charts under stretch: x-labels converted px→% of plot width (one-time JS), the trace SVG
width:100% + preserveAspectRatio="none", dot/halftone zones width:auto;right:Npx.
- Verify the regression baseline at the STRETCH-FREE aspect window (W:H exact, e.g.
1840x1320 for 920x660) where elastic deltas are zero.
B. Mobile app
ONE interactive phone — never a static multi-phone showcase, even if the reference shows three phones. Realistic bezel (radius ~54, side buttons, punch-hole/notch), status bar treatment matching the reference, home indicator. Stage centered on a soft backdrop, scaled by contain-fit to fill the window.
- All reference screens live INSIDE the phone, navigated by ~250ms translateX slides.
- Secondary screens (profile, detail) are OPAQUE
background:var(--bg)screens pushed over
the main with a slight parallax on the one beneath (transparent secondary screens create a see-through bug — always opaque).
- Tab bars / floating navs are shared overlays,
position:absolutein the stage; they may
slide away on pushed/detail screens (back button returns).
- REAL-PHONE MODE (mandatory): on actual phones the fake bezel must disappear:
@media (max-width:560px){
#stage{position:fixed;inset:0;width:100%!important;height:100%!important;transform:none!important}
.phone{position:absolute;inset:0;width:auto;height:auto;border-radius:0;background:none;box-shadow:none}
.bezel-only-bits{display:none}
.screen{left:0;top:0;width:100%;height:100%;border-radius:0}
/* pad chrome with env(safe-area-inset-*) */
}plus <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover"> and a JS guard in fit(): if (matchMedia('(max-width:560px)').matches){ stage.style.cssText=''; return; }
- Internal layout uses flex/percentages off the logical screen width so real phone widths
(375-430) stretch gracefully.
C. Landing page / long page
Normal document flow at the measured design width (usually 1440 logical container).
- Measure heading ink-height PER SECTION — sizes drift between sections; assuming one size
is a classic miss.
- Total page height must land within 1% of (reference height ÷ scale). Reconcile drift
section-by-section with bg-boundary scans, not guesswork.
- Verify composition (esp. hero) at 1280/1440/1600/1728 — no horizontal scroll at any.
- Anchor nav must actually scroll; FAQ accordions single-open; carousels/pagers cycle
content.
D. macOS app
Window on a fixed logical canvas inside the dashboard engine (A) or centered-stage variant. Native feel is the bar: traffic lights (#FF5F57/#FEBC2E/#28C840, 12px, hover glyphs), correct title-bar/toolbar treatment, system font stack (-apple-system/'SF Pro', Inter fallback; Hiragino for Japanese), translucent sidebar per the reference's mode, overlay scrollbars, vibrancy where shown. Content scrolls behind blurred bars.
E. Hero / single section
Fills the viewport via cover-scale stage (max(vw/W, vh/H), centered, position:fixed). Usually pairs with the photographic techniques in 01-measurement.md §5. All UI rebuilt live; interactions included from the start (nav, form validate + confirm).
Layout gotchas (every category — these have all burned a build)
position:fixedsilently breaks insidetransform:scale()stages — every overlay/modal/
toast is position:absolute inside the stage (which also makes them scale correctly).
flex:1on a fixed-height child inside a COLUMN flex container collapses its height —
pair with min-height.
flex:1in an over-constrained row (fixed siblings + margin auto) collapses to ZERO width
— use flex:1 1 auto.
- SVG
url(#id)gradients/defs FAIL silently insidedisplay:nonesubtrees — every SVG
gets its own local defs with unique ids.
- Equal-specificity theme overrides: source order decides — a later
[data-theme] .bar
override can silently erase a base style. Screenshot every page in both themes to catch.
- 1.5px borders render at half-device-pixels; absolute offsets resolve against the padding
box — when a knob/element lands 1px off, measure the real rendered rect in-page and pin it.
- :hover/:active states on every interactive-looking element — a polished page with zero
hovers gets rejected.
- Nav must be genuinely clickable: overlay containers swallow pointer events (z-index traps).
Test with elementFromPoint + real clicks, never assume.
?frozen=1URL param that disables tickers/animations/persistence — deterministic
screenshots depend on it.
Phase 3 — Verification: your diff loop, then the mandatory two-agent review
Step 1 — your own diff loop (before any agent sees it)
Serve: python3 -m http.server <port> (kill stale servers on the port first). Screenshot with Python Playwright:
- exact logical viewport size,
device_scale_factor=2 - ALWAYS
page.wait_for_function('document.fonts.status === "loaded"')before shooting - Playwright wedges after several screenshots in one session — relaunch the browser when it
hangs; never use file:// URLs
- keep a reusable
measure/shot.py
Compare ref vs render:
- per-section crops side by side + ghost overlays (50% alpha)
- numeric landmark scans (ink boxes of headings/numbers, edge positions of cards, bg-color
row profiles for section boundaries)
- targets: structural deltas <=1 logical px; ink ratios 1.00-1.06x; overall mean |d| of
5-15 (the residual is substitute photos). Halftone/gradient surfaces saturate the metric — judge those by sampled colors at grid points instead.
- run the loop on EVERY region INCLUDING the bottom half — the classic failure is seven
loops on the hero and one on everything below.
- real-click test: elementFromPoint on every nav link/button center + actual clicks.
- console: 0 errors, 0 failed requests, at three window sizes.
Iterate until clean. Only then proceed to step 2.
Step 2 — spawn TWO adversarial verification agents (mandatory, every build)
Always two, in parallel, independent. They are told to REFUTE the build, not bless it, and to report ONLY verified findings (crop/measure/reproduce, with numbers or exact repro steps in the evidence). Give both: the file path, the served URL, the original image path, the tooling notes above, a scratch prefix in measure/, and the list of KNOWN INTENTIONAL deltas so they don't report them (substitute photos, added bezel, etc.).
Agent 1 — pixel-fidelity reviewer. Compares every region of the render against the reference image at full resolution: layout geometry (positions/sizes of every block), typography (family/weight via ink-pixel counts, sizes via cap heights), colors (multi-point sampling), photo mood (saturation/luminance band sampling with stated crop boxes), radii, spacing/density, charts (trace geometry). Output: findings with severity (critical/major/minor), exact location (file/selector/region), measured evidence, suggested fix.
Agent 2 — product/structure reviewer. Tries to break it with REAL Playwright clicks (not JS el.click()): every control, nav links (z-index traps), accordions, carousels, hover states (computed-style change check), multiple window sizes, console errors throughout, text legibility over images (contrast ratios), composition at 1280-1728. Same output schema.
Step 3 — adjudicate, then apply
Roughly 1 in 8 verified-sounding findings is FALSE (normalization errors, content the reviewer hallucinated, presentation-canvas confusion). Before applying ANY finding:
- check its evidence against the artifact yourself (one crop or one measurement)
- conflicting findings between the two agents → measure the reference and decide
- reviewer fixes are HYPOTHESES: tune to the measured target, not the prescribed value
(a prescribed saturate(.95) missed the reference's measured saturation; the measured target said 1.5)
Apply the real findings yourself by hand, re-run the diff loop, then present to the user: side-by-side image, the diff numbers, and what the reviewers caught. STOP and wait for approval before the functional phase.
Regression baselines (created at approval, guarded forever after)
- Freeze
measure/baseline_approved.pngat the deterministic state (?frozen=1, cleared
localStorage, stretch-free aspect window for elastic engines).
- Write
measure/regress.py: re-shoot, numpy-diff vs baseline, PASS/FAIL with px counts. - Profile the noise floor by diffing two runs of UNCHANGED code; gate against the floor.
- Flaky gates come from nondeterministic glyph rasterization (icon fonts): mask those glyph
cells or capture twice and require self-consistency.
- From then on: after EVERY edit, the gate must pass before claiming done.
Background-agent hygiene (applies to the verification agents)
- Their briefs must mandate PROGRESS notes if long-running; session limits kill agents
routinely and transcripts are cleaned almost immediately after completion — anything not on disk is gone. Successors spawn fresh from disk records.
- Agents rightly distrust relayed "the user said X" messages arriving mid-run. Put
everything they need in the SPAWN brief (trusted). If user feedback must change a file an agent is actively editing: stop the agent first, apply the change yourself, document it in PROGRESS.md with a do-not-revert note, then respawn with the updated brief.
Phase 5 — Fully functional: design-system extraction and page extrapolation
Starts ONLY after the user approves the clone. The user will usually give ONE screenshot — a dashboard with a sidebar promising five pages, a mobile home screen implying detail screens. You must build ALL of it, coherently. This file is the method that makes invented pages look like the original designer drew them. (Distilled from the Wise build, where one Statistics screenshot became a complete 4-page app the reviewers couldn't tell apart.)
0. The prime constraint: the approved screen is sacred
Before any edit, freeze the regression baseline + gate (03-verification.md). The approved pixels must stay identical within the noise floor for the ENTIRE phase. Concrete rules that protect it:
- New themes/views/overlays are purely ADDITIVE (
[data-theme]blocks, hidden-at-rest DOM).
Never refactor a verified value into a variable "for cleanliness" — that shifts rendering.
- After every structural change, re-run the gate. When the gate catches a 1px drift, the
REFERENCE wins over your "correct" formula — pin the measured offset.
- Pristine state (cleared localStorage) must render the approved screen byte-identically;
toggling any feature off must restore byte-identical markup (cache pristine innerHTML).
1. Extract the design system — the reference is a rulebook, not an impression
Before designing a single new element, write down what the known screen PROVES:
Layout grammar. The grid (card sizes, gutters, radii). Which slot is allowed to break rhythm (a spanning card, a hero photo) — new pages earn exactly the same number of special slots, never more. Global chrome (sidebar/header/tab bar) never changes between pages; only the title/active state swaps.
Color budget per screen. Count the reference's ratio (e.g. exactly 1 yellow card + 1 light card + 3 dark + 1 photo). Every new page repeats that ratio — this single rule is most of what makes a new page read as "the same app". Accents that appear once stay scarce: if lavender exists for exactly one job (tooltips), it never gets a second job.
Typography scale. List every size/weight pair on the reference. New elements must pick from the existing scale — no new sizes invented, ever.
Component library. The reference contains, hidden in plain sight, a component set: list every distinct anatomy (legend row, chip group, big-stat + divider + sub, icon tile header, bar chart with its selected-state treatment, pill button, split button, circle arrow button...). THE RULE: new pages may only recombine these existing components. If a need can't be met by recombination, derive the new component from an existing one and say which (a checkbox = the chip's border + the accent fill state; a progress track = the toggle pill flattened; a calendar "today" cell = the selected chip shrunk). Nothing on a new page should exist that the original designer didn't already draw.
Implied behavior contracts. Static components imply behaviors, and consistency demands every implied behavior works the SAME everywhere: chips imply selection (ALL chip groups select and drive the stat next to them); a selected-bar treatment implies every chart moves its selection on click with the identical treatment; arrow-circles imply "open detail" in one shared modal skin. A group left decorative while its siblings work breaks the contract — reviewers (and the user) catch it.
2. Assign content to pages
The chrome already promises the destinations (sidebar labels, tab bar icons). Honor what each label would mean in the real product category: history pages want lists (the natural spanning slot — lists want height), schedule pages want calendars, settings pages want the identity moment (the avatar belongs on the brand-color slot) + toggle rows + the plan/money card on the same prominence slot the reference gives its most important number.
Run the symmetry check on purpose: each new page = the same color budget, the same number of spanning slots, the same chart-family count as the reference. Unique hero elements (a photo card) stay unique to the original screen — that preserves its hero status.
3. Data must look lived-in, not generated
Real products have awkward, specific numbers:
- Calendars astronomically correct (the demo date's weekday must be real). Week numbers ISO.
- Modal/detail values must AVERAGE to the card's headline number.
- Lists: asymmetric amounts, dates thinning out as they get older.
- NEVER copy-paste a ratio from another card (a duplicated 78/22 split is a "fake-data
tell" reviewers catch). Every number gets its own plausible value.
- Deterministic: fixed arrays or seeded generators only (no runtime Math.random) — the
regression gate and the assertion suite depend on reproducibility. Chart ranges (1d/1w/1m) get one fixed seed each: same seed = same path, restoring the default range = byte-exact pristine path.
4. Behavior rules
- Every control real, no dead taps: toggles toggle (and restore byte-identical markup when
off), search filters with honest empty states, pickers pick, steppers step with bounds.
- Flows COMPLETE: booking/checkout/trade flows end in a confirmation state, and committed
state is SNAPSHOTTED — a confirmed booking/order must NOT rewrite when its source inputs change afterwards (cancel + redo is the only path). This bug class ships constantly.
- Calculators compute both directions live with editable amounts; the action verb derives
from the actual direction so the button never lies.
- Exports produce real files (real CSV/JSON download, not a toast). "Reset" really wipes.
- Demo features carry honest copy ("canned replies, not live AI", "illustrative demo data").
- Persistence: one namespaced localStorage key (app.v1) holding view/tab/inputs/toggles/
playback state; reload-mid-state restores everything; ?frozen=1 = no restore, no tickers, no writes (deterministic mode). Ship with storage pristine.
- Light/dark theme when present: derived by ROLE inversion, not redesigned (bg↔near-white
same hue temperature, cards→white, grays remapped, accents kept — but thin accent strokes that glow on dark need a DEEPENED variant on light; filled accent surfaces can stay). Additive override block only. Screenshot every page in both themes — equal-specificity source-order bugs are invisible otherwise.
5. Verify the functional build
Playwright assertion suite covering EVERY behavior: each view's controls, flow completions with computed-value checks (assert the actual math), persistence across reload-mid-state, pristine first-run, rapid-click abuse (0 new errors), duplicate-ID scan, byte-level export checks, both window sizes, both themes. Console 0 throughout. Then the regression gate LAST.
Then two adversarial reviewers again, split differently than phase 3:
- one purely FUNCTIONAL (cleared storage, asserts behaviors end-to-end with real clicks)
- one purely DESIGN-CONSISTENCY (screenshots every page × both themes × two window sizes +
every modal/popover, judged against the APPROVED SCREEN as the source of truth — hunting anywhere the recombination leaked: new sizes, broken color budget, off-scale type, contract-breaking decorative controls)
Adjudicate (1-in-8 rule), apply, re-run the suite and the gate, deliver with a short report: behaviors added view-by-view, assertion counts, gate numbers, console status.
Why this works
Not taste — extraction. Measure the one screen hard enough and it tells you the grid, the color budget, the full component set, the type scale, and the implied interaction rules. Inventing the rest is recombination under those constraints, with reviewers hunting for leaks. The unknown pages look right because almost nothing on them is new.