About figma-pixel-verify
Figma Pixel Verify is a skill for checking a running UI against a Figma inventory and specification. It measures visual properties, records evidence and verdicts, and requires non-passing rows to be fixed and remeasured during the run.
Key features
- 01Measure running UI against Figma specs
- 02Reconcile source elements with spec rows
- 03Capture screenshots and pixel-difference evidence
- 04Read computed styles and layout dimensions
- 05Fix and remeasure failed rows
Use cases
- 01Verify a UI after Figma-based implementation
- 02Check design fidelity on a running preview
- 03Record measured results in a living spec
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ fudesign2008/ open-skills --skill figma-pixel-verifyAfter installing, type/figma-pixel-verifyin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
reference.mdevals/evals.jsonscripts/check-figma-verify-report.mjscheck-figma-verify-report.test.mjsDetails
From the project READMEFigma Pixel Verify
Measure a running UI against a Figma living artifact (Inventory + Spec) and write pass/fail evidence into its Verify section (or a Spec source sibling). On a non-pass row, fix and remeasure in this run. Actual values come from a laid-out read. Do not overwrite Figma expected. Do not edit figma-pixel-implement skill prose.
When to use
- After
figma-pixel-implement(or any Figma implement pass that left a durable inventory + spec path) - User asks to check pixel alignment / design fidelity / 对照稿面验收
- Standalone: user provides Figma URL + running preview URL/route; persist a minimal inventory + spec to the same path convention before measuring
Inputs
- Living artifact — File path from implement. Same columns as implement reference (
state,mode,basis). After an implement run this session, do not silently rebuild a missing table and claim that handoff complete—FAIL and return tofigma-pixel-implement. - Runnable UI — Document surfaces, preferred first: a component-workbench story from the living artifact’s Stories subsection (or a detected Storybook-style workbench) → a lightweight isolated harness (stub out host/engine dependencies) → the in-product preview route → a full app/browser launch (heaviest, last resort). Surfaces with no computed-style API use
layout-probe: a command that mounts the control in the shipping parent (the container the screen uses), runs layout, and prints frames. A command that lays out the control with no parent is not a measurement. - Running-UI screenshot when the channel can open a UI (
storybook,isolated-harness,preview,full-app): save a png, jpg, jpeg, or webp of that UI this run and cite its path. Alayout-probethat cannot open a UI recordsScreenshot: not-openableand does not invent an image. The image is supporting evidence. It does not replace numeric checks.
Workflow (ordered)
- Preflight — Resolve the measurement channel per the ladder in reference.md. Document UIs: Stories-subsection story → detected component workbench → in-product preview → full app launch (last resort). Surfaces with no computed-style API, or with every document rung unavailable:
layout-probe— a command this run executes that mounts the control inside the shipping parent (the same container the screen uses), runs one layout pass, and prints the control's width, height, and child frames. A command that lays out the control with no parent is not a measurement. Build the smallest probe the project toolchain can run when none exists, then run it. If no workbench exists, make one advisory adoption suggestion; if the workbench is broken, fall down the ladder and record it; declare the channel in the report (storybook | isolated-harness | preview | full-app | layout-probe). Confirm the chosen channel is reachable. ResolveSpec source(living-artifact path). The Inventory plus Spec is the list of rows to measure. If this run already implemented from Figma and the file is missing or critical rows are absent: Overall FAIL, return tofigma-pixel-implementto complete the table—do not invent rows and claim handoff complete. If this is standalone (no implement this run): persist a minimal inventory + spec using implement’s path convention, then measure. Note theme scope (mode) and visualstates. When multiple themes are in scope, identify how the running UI switches themes (data-theme, toggle, system preference) before measuring. Browser channel (quality gate): On macOS, if theego-browserskill (or its install script) is available and the CLI is already usable, or the install finishes in this turn without waiting on a person, use it before other document channels. If install still needs GUI onboarding, record that and continue with the next reachable JS-eval or layout-probe channel in this turn. Do not copy that product’s TaskSpace/CLI API into this skill. If the OS is not macOS, the skill/install path is absent, or install is blocked, continue with another channel and record which one was used. Missingego-browserdoes not abort verify. - Source reconciliation (gate) — Before any measurement, mechanically derive the expected-item list from the Figma source (fetched node-tree data and/or the reference exports): every in-scope node's position, size, typography set, container properties, and sibling gaps becomes an expected item. Reconcile it against the Spec table — every expected item MUST be covered by a spec row or listed as
out-of-scopewith a reason; gaps MUST be added as new spec rows (they become rows to measure in this run) or explicitly markedout-of-scope. Write the reconciliation table (expected items / covered / added rows / out-of-scope) into the Verify report. Measurement MUST NOT begin before reconciliation completes. When an in-scope frame lacks its reference export, overall is FAIL and control returns tofigma-pixel-implement. - Vision — When the model can inspect images, capture UI vs Figma screenshot; note gross mismatches (wrong component, missing block, theme drift, fragment-cut / stacking look). Vision is supporting evidence. When JS-eval or a layout probe can run, vision-only comparison does not produce Overall PASS. On an openable channel, keep the saved running-UI file and cite it. On a layout probe that cannot open a UI, record
not-openable. - Pixel differential sweep — Compare each reference export against this run's implementation screenshot as a same-box side-by-side (and a per-pixel difference artifact when the toolchain allows); inspect region by region. Every observed difference becomes either a new measured row or a recorded residual — differences found outside the spec table are this sweep's legitimate product. Save the sweep artifacts (side-by-side and/or diff images) and cite their paths in the report; for an openable channel without sweep artifacts, overall is not PASS.
- Numeric measurement — For each critical visual row, switch the running UI into that row’s
stateandmode(prefer a real pointer hover/click/theme control through the browser channel — a declarative story entry (test id, knob, auto-action) counts as reaching the state — then read computed style and/or bounding box). Onlayout-probe, actual is the printed frame from that command. A source literal written to matchexpectedleaves the row unmeasured. Map selectors carefully; prefer stable test ids when present. Default-state readings do not satisfy a hover (or other state) row. Shipped-reference interactions: when the task text containsfigma.comornode-id=and also参考,match the existing, orsame as, read the named existing control's authored rules for hover, focus, focus-within, and open or expanded. Each property that differs from the resting rule becomes a spec row with thatstate. A resting-frame reading does not pass those rows. A probe that only checks text, visibility, or item count does not measure them. Per-mode rows under different themes are separate verdicts. Skip numeric PASS claims forout-of-scope: behaviorlines — list them as behavior, not pixel PASS. For every multi-line text node in scope, measure beyond the box: line count, each line's ink start position (the rendered left edge of its first visible character), the break content, and any hanging indent or in-line offset — record them as rows; a box-only reading does not count as coverage for a multi-line node's alignment. - Compare — Apply tolerances in reference.md. Assign a verdict per row. Unmeasured critical visual rows (JS-eval was available but the row was skipped) are blocking, not a path to Overall PASS via residuals.
- Fix loop — On a non-pass row, fix the running UI and remeasure in this run, then update actual/verdict columns. Continue until overall PASS or the measurement command cannot be executed. Safety cap: about 8 iterations; hitting the cap is overall FAIL with residuals listed. When the measured box matches a platform control’s intrinsic size and not the spec, the next iteration switches that control to
custom-layoutand assigns the spec frames in the layout pass. Do not change Specexpected. Do not patchfigma-pixel-implementskill files. Do not pause for a person to judge pixels. - Independent re-check — Before writing overall PASS, obtain an independent re-check: a fresh-context agent run, a different model, or a blind re-source check that re-derives the expected-item list from the Figma source without consulting the author's Spec table. Record the method, the re-checker identity, and its artifact path as an
Independent check:line in the report. When no independent channel is available, record the attempted methods and the unavailability reason — never claim a check that did not run. - Report — Write the Verify section of the living artifact (or the sibling report named in Spec source). Template: reference.md. Include coverage (measured vs inventory), the channel declaration (
Channel: storybook | isolated-harness | preview | full-app | layout-probe), and, when the project declares a host engine older than the workbench browser, an optional note of that difference. The note does not withhold geometric PASS and does not create a human step. Rows that depend on that engine's syntax are measured with a runtime or parser at that floor, or they stay unmeasured and overall is not PASS. Component-test results may be cited as supporting evidence. For multi-theme scope, report per theme. That Verify section is the measured report hosts require. Overall is PASS only when every critical visual row in scope is PASS oraccepted-residualand each actual cites a laid-out read from this turn, and the Source reconciliation table is present and complete, and the pixel-differential-sweep artifacts are cited, and theIndependent check:line is present (or its recorded unavailability with attempted methods). Overall is not PASS if any critical row is unmeasured,MISSING-style, unacceptedDRIFT,HARDCODEDwhen the spec required a token, the spec itself is incomplete, actual was copied from a source literal, or the running UI shows an invented solid fill against Spec transparent/none, a fragment-cut graphic against a single Assets export, a generic wrapper that does not match instance geometry, a self-invented text-color alias when Spec recorded a Figma variable, or a visible graphic still on a remote design URL. When the probe command cannot run, overall is FAIL and the report names that command. Before reporting PASS, runscripts/check-figma-verify-report.mjsin this skill directory on the living artifact. Exit 0 is required. The Verify section must includeMounted in,Measurement command,Screenshot, and a### Measurement excerptcode block whose text contains every passing row's actual numbers and colors. Forstorybook,isolated-harness,preview, andfull-app,Screenshotis a path to a non-empty png, jpg, jpeg, or webp saved this run. Forlayout-probethat cannot open a UI,Screenshotisnot-openable. The checker resolves a relative path from the report file's directory and rejects a missing or empty file. A screenshot file does not replace the numeric pass.
Verdict taxonomy
| Verdict | Meaning |
|---|---|
| PASS | Within tolerance of expected, measured in the correct state / mode |
| DRIFT | Measurable but outside tolerance |
| HARDCODED | Value matches by luck or literal, not via project token/variable when the spec required a token — flag when token mapping was part of the contract |
| VARIANT | Matches a different documented variant (e.g. hover/dark) than the requested one |
| MISSING-style | Property not measurable (no node, no style, or channel unavailable) |
| accepted-residual | Explicit frozen deviation (owner + reason). Not PASS against the Figma expected |
Tool intent (platform-agnostic)
| Intent | Agent may use |
|---|---|
| Open/preview UI | Quality channel after the macOS install gate; otherwise another JS-eval / preview channel the Agent already has |
| Switch visual state | Real pointer hover/click/theme control, then read styles |
| Read laid-out frames | JS evaluate in page, DevTools-like APIs, or a layout-probe command that prints frames |
| Screenshots | Browser or Figma screenshot tools (vision inspects them when the model can see images) |
| Apply fixes | Native edit tools, then re-measure |
After a successful quality-channel install, use that channel for measurement. When install is impossible, another JS-eval path is valid. Do not copy a third-party browser API into this skill.
If no document channel exists, run a layout probe and read its printed frames. If that command cannot be executed, label numeric rows MISSING-style, name the missing command, and do not claim pixel PASS.
Relationship
| Skill | Boundary |
|---|---|
figma-pixel-implement | Produces code + durable Inventory/Spec; does not own final verdict |
| Agent-native Figma→code guidance / MCP fetch | Not a substitute for measurement |
| Host PDCA workflows | Strong-depend at install; invoke when Figma UI is in scope; missing Verify section (or Spec source sibling) fails that verification stage; spec-gap FAIL re-enters implement |
ego-browser (external, macOS) | Quality measurement channel: install when the skill/install path exists; not a frontmatter dependency; degrade when not installable |
Pitfalls
- Declaring PASS from screenshots alone when measurement was possible.
- Infinite tweak loops without re-measure.
- Treating ±1 CSS px geometry noise on high-DPR as failure without noting DPR.
- Silent skip of mask/currentColor issues — if implement violated asset rules, call that out as root cause of color DRIFT.
- Marking per-mode rows PASS after measuring only the default theme — each theme in scope is measured under its own theme.
- Marking hover rows PASS from default-state styles.
- Overall PASS while critical rows sit only in residuals as “not measured yet”.
- Treating a frozen 18px vs 16px as PASS instead of
accepted-residual. - Keeping the report only in chat when a living-artifact path exists.
- Overwriting Spec
expectedwith measured actuals. - After this-run implement: silently rebuilding a missing spec and claiming the handoff was complete.
- Marking PASS for an invented solid fill, stacked leaf-path icon, generic 16/24 shell, invented text-color alias, or remote design URL.
- Skipping a usable quality-channel CLI on macOS, then measuring only through a weaker document channel.
- Waiting on GUI onboarding while another channel or a layout probe can run in this turn.
- Treating a source literal that matches
expectedas a measured actual. - Copying a third-party browser TaskSpace/CLI API into this skill.
- Aborting verify because
ego-browseris unavailable on a host that cannot install it. - Declaring Overall PASS from vision/screenshots alone when JS-eval was available.
- Reporting a verdict without declaring the measurement channel used.
- Treating a workbench/engine-floor note as a reason to withhold geometric PASS or to ask a person to finish those rows.
- Treating a layout command that never mounts the shipping parent as a measurement.
- Reporting PASS for an openable channel without a saved non-empty screenshot file.
- Inventing an image for a layout probe that cannot open a UI.
- Skipping source reconciliation — the measured set then equals whatever the author remembered to write down.
- Reporting PASS without differential-sweep artifacts on an openable channel.
- Treating same-context self-review as an independent re-check.
- Measuring a multi-line text node's box only and calling its alignment covered.