BXUP Track progress & test yourself — free
Frontend & Mobile · Playbook · Core · ~4 min read
FRONTEND & MOBILE · PLAYBOOK · CORE

Diff Visual Fidelity with a Multimodal Model

Eyeballing "close enough" is how 4px drifts and off-token grays reach production. Screenshot the rendered implementation, screenshot the Figma frame, and have a multimodal model produce a structured discrepancy list — spacing, type scale, color, states — then fix, re-shoot, and repeat until the table is empty or intentional.

Tier: Core · Workflow area: Design-to-code & design systems · Tools: Claude / Gemini vision, Playwright screenshots, chrome-devtools-mcp

Human visual review fails predictably: we normalize what we've stared at, we miss systematic-but-small errors, and we never check all states. A vision model comparing two images side by side has no such attachment — it will flatly report that the card padding is 20px against a 24px design, in every variant, every time you ask.

When to use

  • Closing the loop on design-to-codethe verification half of pb-09/pb-12, and the review step for any AI-generated UI that had a design.
  • Design QA before handoffcatching drift before the designer does, with evidence instead of vibes.
  • Migrations that must be pixel-faithfulTailwind upgrades, CSS refactors, design-token sweeps where "nothing should change visually."
  • No visual-regression infra exists yetthis is the zero-infrastructure version of Chromatic-style review (pb-26 is the systematized sibling).

When NOT to use

  • No reference existsfreestyle UI has nothing to diff against; use design-review judgment instead.
  • Sub-pixel or antialiasing-level precisionvision models reliably catch 2px+ drifts, wrong tokens, and wrong weights; they are not pixel-diff algorithms. For exact-pixel gates, use Playwright's toHaveScreenshot and keep the model for interpreting diffs.
  • The design is deliberately loosewhen the frame is a sketch, structured diffing generates noise, not signal.

The recipe

  1. Capture both sides under controlled conditions. Same width, same data, same theme — or the diff is meaningless. Implementation side, via a Storybook story or live route:

    npx playwright screenshot --viewport-size=1280,900 \
      "http://localhost:6006/iframe.html?id=subscriptioncard--active&viewMode=story" \
      shots/impl-active.png
    

    Design side: export the frame from Figma at 1x (select frame → Export → PNG), or let the Dev Mode MCP fetch the screenshot if wired (pb-09). Name files by state: figma-active.png, impl-active.png.

  2. Ask for a structured discrepancy table — never "do these match?" Open-ended comparison prompts produce reassurance; structured prompts produce findings:

    Image 1 is the design (Figma export). Image 2 is my implementation.
    Compare them systematically and output a markdown table:
    | # | element | property | design value | implementation value | severity |
    Check in this order:
    1. Layout: alignment, gaps between siblings, padding at each edge
    2. Typography: size, weight, line-height, color per text element
    3. Color: backgrounds, borders, icons — name the closest token if you
       can tell values apart
    4. Shape: radii, borders, shadows
    5. Content: truncation, icon choice, badge text casing
    Severity: must-fix (visibly wrong) / minor (2-4px or shade-level) /
    check-token (values differ but both look like system values).
    Do NOT say "looks close" — if a row has no discrepancy, omit it.
    

    Good output is 5–15 concrete rows ("Badge text: design 12px/600, impl 14px/500 — must-fix"), not prose. If you get prose, restate: "Table only, omit matching elements."

  3. Convert findings to fixes — mapped to tokens, not magic numbers. Feed the table back to your coding agent:

    Fix rows 1, 3, 4, 7 of the discrepancy table in SubscriptionCard.tsx.
    Every fix must land on a design token: e.g. row 3's 20px padding
    becomes p-6 (24px) per our scale — never a raw pixel value copied
    from the design. If the design value has no matching token, flag it
    for the designer instead of hardcoding.
    
  4. Re-shoot and re-diff until the table converges. Loop steps 1–3. Convergence means every remaining row is classified intentional with a reason ("radius token differs from design; DS decision 2026-03"). Two to three iterations is typical. Then run the loop for every state: hover (capture via Playwright's page.hover in a small script or chrome-devtools-mcp driving the real page), error, empty, dark mode, and your narrowest supported width.

  5. Attach the final table to the PR. The converged table — with intentional-deviation reasons — replaces "matches design ✅" in the PR description. Designers review deviations, not pixels; reviewers see evidence, not claims. For recurring surfaces, commit the capture script so the next change reruns the same loop in one command:

    sh scripts/fidelity-shots.sh subscription-card && open shots/
    
Note

Control the capture environment ruthlessly: identical viewport, deterministic data (MSW fixtures, frozen dates), fonts fully loaded (Playwright waits, but check custom-font FOUT), animations disabled (prefers-reduced-motion or a global CSS override in the story). Half of all "discrepancies" in sloppy setups are environment noise — and noise teaches the team to ignore the table.

Make it yours

  • Web, any frameworkthe loop is markup-agnostic; only the capture URL changes (Storybook iframe, Vite dev route, or production preview deploy).
  • React Nativecapture with xcrun simctl io booted screenshot shots/impl.png (iOS) or adb exec-out screencap -p > shots/impl.png (Android); diff against the mobile frame export; add a device-width pass since RN layouts drift differently per screen class.
  • Fluttergolden files already give you pixel-exact gates (flutter test --update-goldens); use the multimodal loop above goldens to interpret failures and to compare against Figma, where goldens can't help.
  • CI-integrated variantnightly job screenshots key stories, sends design/impl pairs to the vision API, and posts the table to Slack; drift gets caught while the context is fresh, not at design QA.

Quality check

  • Discrepancy table convergedevery row fixed or explicitly intentional with a reason; no "looks close" rows.
  • All states diffed, not just the defaulthover, error, empty, dark, narrow width each have a converged table or an N/A note.
  • Fixes landed on tokensthe diff contains no new raw hex/pixel values introduced to "match the design."
  • Capture is reproduciblescript committed; a teammate can regenerate identical shots.
  • Designer reviewed the intentional deviationssign-off threaded on the PR, closing the loop.
Key Takeaway:

Replace "does this match?" with a converging discrepancy table: controlled screenshots in, structured findings out, token-mapped fixes, repeat. The vision model supplies tireless eyes; the discipline of the loop supplies the fidelity.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in