Compare a Figma design to a live implementation. Produces a structured comparison report with screenshots saved to disk. Trigger phrases: "compare to Figma", "compare to the design", "compare to the mock", "visual comparison", "check against Figma", "match the mock", "design diff", or a Figma URL with a request to inspect or match a UI element.
How this skill is triggered — by the user, by Claude, or both
Slash command
/compare-figma-to-impl:compare-figma-to-implgeneral-purposeThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Systematically compare a Figma design to a live Firefox implementation
Systematically compare a Figma design to a live Firefox implementation using the Figma MCP and Firefox devtools MCP. Produce a structured comparison covering layout, structure, and styling.
YOUR #1 JOB: End every successful run by saving the report to
comparison/report.mdvia Bash with a single-quoted heredoc. Do NOT use theWritetool — Claude Code blocks subagent Writes that look like report files. See Phase 7 for the exact steps and Report Schema for the content shape.
FIGMA_TOKEN must be set in the environment (see .env.sample)/open-ai-window or /open-smartbar-dropdown first if needed)Call mcp__plugin_figma_figma__get_screenshot to capture the
design visually. Always do this — it's the ground truth for your
own visual analysis.
Download the Figma screenshot to disk using the Figma REST API
so it can be embedded in the report. Parse fileKey and nodeId
from the Figma URL, then run:
source .env 2>/dev/null
mkdir -p comparison
IMG_URL=$(curl -sH "X-Figma-Token: $FIGMA_TOKEN" \
"https://api.figma.com/v1/images/${FILE_KEY}?ids=${NODE_ID}&format=png&scale=2&contents_only=false" \
| python3 -c '
import sys, json
try:
data = json.load(sys.stdin)
except Exception as e:
print(f"ERROR: Figma API returned non-JSON: {e}", file=sys.stderr)
sys.exit(1)
err = data.get("err")
if err:
print(f"ERROR: Figma API: {err}", file=sys.stderr)
sys.exit(1)
images = data.get("images") or {}
url = next(iter(images.values()), None) if images else None
if not url:
print(f"ERROR: Figma API returned no image URL. Response: {json.dumps(data)[:300]}", file=sys.stderr)
sys.exit(1)
print(url)
')
if [ -z "$IMG_URL" ] || ! echo "$IMG_URL" | grep -q '^http'; then
echo "FATAL: Could not get image URL from Figma (see error above)." >&2
echo "Common causes:" >&2
echo " - FIGMA_TOKEN is missing, expired, or invalid" >&2
echo " - The token lacks access to this Figma file" >&2
echo " - The node_id format is wrong (must use ':' not '-', e.g. '1:42' not '1-42')" >&2
echo "Fix: Verify token at https://www.figma.com/developers/api#access-tokens and confirm file access." >&2
exit 1
fi
curl -sL -o comparison/figma-screenshot.png "$IMG_URL"
Note: convert node-id URL param format (1-42) to API format
(1:42) by replacing - with :.
Run this script exactly as written — do not rewrite or simplify it. The error guard must execute so the user sees the actual cause.
If this command exits non-zero, STOP. Do not proceed to Phase 2.
Do not continue without comparison/figma-screenshot.png on disk.
Report the script's stderr output verbatim to the user (it names the
specific cause — token issue, file access, wrong node ID, etc.) and
end the comparison.
Call mcp__plugin_figma_figma__get_design_context with
forceCode: true to get the generated code and style tokens.
Extract from the Figma output:
NEW/Font/Size/Large/bold)Record sub-node IDs: From the get_design_context response, note
the Figma node ID for each distinct child element (icon, title text,
button, separator, description text, etc.). These IDs are needed in
Phase 5 to capture focused per-discrepancy Figma screenshots. Store
them as a mapping of element label → node ID (e.g. icon → 1:234,
title → 1:235).
Use mcp__firefox-devtools__evaluate_chrome_script to extract live data.
All scripts must be wrapped in an IIFE: (function() { ... })().
Navigate the DOM to find the target element. For AI window components, the typical path is:
gBrowser.selectedBrowser.contentDocument
→ querySelector('ai-window')
→ .shadowRoot
→ target element (may have its own shadowRoot)
Collect: tag names, class names, shadow DOM boundaries, child hierarchy.
Before extracting computed styles, dump outerHTML (truncated) of
each key element — especially icons, images, and interactive controls.
This reveals the actual element type (is it an <img src="...">, a
<div> with background-image, an inline SVG, etc.) and determines
what to extract in subsequent steps. Without this, scripts that assume
one structure (e.g. el.querySelector('img')) will silently fail when
the element IS the <img> tag. Example:
el.outerHTML.substring(0, 400)
Use contentDoc.defaultView.getComputedStyle(el) to read live values.
Do NOT read source CSS files — use live computed styles only.
Extract these properties for each relevant element (see
references/comparison-checklist.md for the full list):
--var values relevant to the componentAlso call getBoundingClientRect() on each element for actual pixel
dimensions and positions.
Inspect ::before and ::after pseudo-elements — they often implement
separators, icons, or decorative elements:
contentDoc.defaultView.getComputedStyle(el, '::before')
When a property uses var(--foo), verify the variable resolves at the
element where it's consumed, not just where it's defined. CSS custom
properties inherit through shadow DOM, but the resolved value may differ
from expectations. Check with:
getComputedStyle(el).getPropertyValue('--foo')
For every element that renders an icon or image, extract HTML attributes
in addition to computed styles. CSS properties alone cannot identify
what image is being displayed when the element is an <img> tag.
For each icon/image element, collect:
tagName (is it img, div, svg, etc.?)src attribute (for <img> elements)alt attributeouterHTML (truncated to 400 chars)backgroundImage computed style (for CSS-based icons)-moz-context-properties and fill (for SVG icons styled via CSS)opacitySee references/comparison-checklist.md for a reusable script.
After capturing both screenshots (Figma in Phase 1, live page via
mcp__firefox-devtools__screenshot_page or
mcp__firefox-devtools__screenshot_by_uid in Phase 2), save the
implementation screenshot to comparison/impl-screenshot.png, then
compare them visually before proceeding to numerical comparison.
Look for obvious differences that CSS property extraction might miss:
Note any visual discrepancies found here. They guide where to focus the detailed numerical comparison in Phase 3, and serve as a safety net for extraction bugs that silently drop data.
If the full-page screenshot is too large, capture element-level
screenshots using screenshot_by_uid for the specific component.
Produce two comparison sections. Use tables for clarity.
Compare ALL visual properties in a single section. Cover:
Do NOT create a separate "Styling Details" section — merge all styling comparisons into this Layout & Styling section.
Categorize each difference into one of three buckets:
border-radius: 9999px vs 24px when both produce a pill at that
height; box-shadow vs border producing the same visual)For each discrepancy identified in Phase 4, capture a focused screenshot of the relevant area from both Figma and the implementation.
mcp__plugin_figma_figma__get_screenshot targeting that
node's ID (recorded in Phase 1 step 5).scale=4) for a zoomed view that better shows
the detail.NODE_ID with the specific
node, keep the same FILE_KEY).comparison/figma-{slug}.png where {slug} is a short
kebab-case label derived from the discrepancy (e.g. border-color,
icon-size, title-font).mcp__firefox-devtools__take_snapshot — the Phase 2
snapshot may no longer be in scope and screenshot_by_uid requires
current UIDs.mcp__firefox-devtools__screenshot_by_uid with the saveTo
parameter targeting the specific DOM element involved in the
discrepancy.comparison/impl-{slug}.png using the same slug as the
Figma side.screenshot_by_uid on the
nearest parent element that visually shows the effect.border-color, icon-size, title-font-weight).figma-screenshot.png /
impl-screenshot.png) remain unchanged — they are still used in
Phase 2.5.Before presenting results, review the analysis for common errors:
background-image is none but position/size
are set, check whether the CSS variable value is missing url() wrapping.
A bare URL string (without url()) is NEVER valid for background-image.var(--foo) actually
resolves at the consumption site, not just at the definition site.search-glass.svg) instead of the
context-appropriate icon (e.g. the configured search engine's favicon).
Always verify the actual icon src — don't assume it's correct just
because a 16x16 placeholder is rendering.This is the final step. The deliverable is the file on disk, not the conversation text.
Use Bash with a single-quoted heredoc to save the full markdown
report to comparison/report.md. The content shape is defined in
the Report Schema section below.
mkdir -p comparison
cat > comparison/report.md <<'REPORT_EOF'
# Figma vs. Implementation: <subject>
... full markdown body, following the Report Schema ...
REPORT_EOF
The single-quoted delimiter (<<'REPORT_EOF') is required: it
prevents shell expansion of backticks, $, and backslashes that
appear naturally in markdown. Do NOT use the Write tool —
Claude Code blocks subagent Writes that look like report files,
and your write will fail.
Run Bash: ls -la comparison/report.md as a self-check that the
file exists.
Reply with a ≤5-sentence summary:
Do NOT print the full report content to the conversation — the file on disk IS the deliverable.
Note: the ls in step 2 is a nudge for your own check. The actual
enforcement is a SubagentStop hook shipped with this plugin — it
blocks the subagent from stopping until comparison/report.md exists
and is non-empty.
The report at comparison/report.md must be a structured markdown
document using these section headers (each one a literal ## heading
in the report):
## Context — What's being compared: the Figma node, the live UI
element, and any relevant URLs or selectors.
## Summary of Discrepancies — Categorize each finding as
Critical, Minor, or Non-issue under ### Critical, ### Minor, and
### Non-issue sub-headings.
Each individual discrepancy must have its own screenshot table showing the relevant area, with the Figma crop on the left and implementation crop on the right. Use unique filenames per discrepancy:
1. **Border color mismatch**: Description of the issue...
| Figma | Implementation |
|:---:|:---:|
|  |  |
Every severity level (Critical, Minor, Non-issue) gets screenshot tables. Sections with no discrepancies should contain only "None.".
If the user asks for a plan to fix, add a ## Changes section with
specific file paths, line numbers, and code snippets using design
tokens where available.
## Layout & Styling — Table(s) comparing all visual properties:
layout (dimensions, spacing, positioning, flex/grid), styling (colors,
borders, typography, shadows, opacity) — between Figma and
implementation.
references/comparison-checklist.md — Full property checklist and
reusable chrome script patterns for extracting computed stylesProvides UI/UX resources: 50+ styles, color palettes, font pairings, guidelines, charts for web/mobile across React, Next.js, Vue, Svelte, Tailwind, React Native, Flutter. Aids planning, building, reviewing interfaces.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
npx claudepluginhub dmose/compare-figma-to-impl --plugin compare-figma-to-impl