figma-diff
Allows comparing a live web page against a Figma frame, reading layer values from Figma and DOM values from the page to report design discrepancies such as missing elements, visibility issues, and style property differences.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@figma-diffcompare my live site at localhost:3000 against Figma node 4029:12345"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
figma-diff
Compare une page web a une frame Figma et renvoie un verdict machine. Concu pour etre appele par un agent en boucle : il lit Figma et le DOM lui-meme, et ne renvoie qu un resume borne. Ni maquette ni capture d ecran ne transitent par le contexte du modele, donc le cout d un appel ne depend pas de la taille de la page.
Bun + TypeScript 7. Pas de Node.
Comment il lit la maquette
Pas d API REST Figma (6 requetes par mois en gratuit), pas de protocole MCP dans le chemin des
donnees. L outil interroge directement l endpoint HTTP /rpc que le leader de
figma-mcp-bridge expose sur localhost:1994,
celui qu utilisent ses propres instances MCP suiveuses. Les donnees viennent du plugin ouvert
dans Figma desktop : pas de jeton, pas de quota, et la maquette lue est celle que tu as sous les
yeux, modifications non enregistrees comprises.
Figma desktop ──plugin──▶ bridge :1994 ──/rpc──▶ figma-diff ──▶ report.json
▲ │
page live ──────Chromium headless────────────────────┘ ▼
l agentIl faut donc que le serveur MCP figma-bridge tourne et que le plugin soit ouvert dans le
fichier. Sinon l outil echoue avec un message qui le dit.
Related MCP server: DesignDiff MCP
Installation
bun install
bun run browser # telecharge Chromium pour PlaywrightServeur MCP
Declare-le aupres de ton agent, a cote de figma-bridge :
{
"figma-diff": {
"command": "bun",
"args": ["run", "/chemin/absolu/vers/figma-diff/src/server.ts"]
}
}Un seul tool, compare_page_to_frame. Entrees : url, node, map, et en option fileKey,
width, height, pixel, port, top, out.
{
"conforme": false,
"ecarts": 12,
"calques": 34,
"pixelRatio": null,
"issues": [{ "layer": "Hero/CTA", "selector": "button.cta", "prop": "padLeft",
"design": 24, "web": 16, "delta": -8 }],
"unmappedLayers": ["Hero/Badge"],
"rapport": "/tmp/figma-diff-4029-12345.json"
}Trois regles tiennent ce contrat. conforme: false n est pas une erreur : isError est reserve
aux pannes reelles, bridge eteint, node introuvable, page morte — un agent qui recoit une erreur
relance au lieu de corriger. delta vaut toujours web moins design, donc negatif signifie trop
petit cote web. issues est plafonne a top, mais rapport pointe toujours vers le fichier
complet, ce qui garde le cout d un appel constant quelle que soit la page.
L orchestration n appartient pas a cet outil. Quand rappeler, quand abandonner, c est le harness ou l agent qui decide.
Ce que l agent lit
Le README ne lui est pas adresse : sa seule documentation est le schema du tool. Tout y est donc
porte, la marche a suivre pour obtenir un id de frame, la maniere de construire la carte, et la
signification de chaque champ de sortie. Deux points comptent plus que les autres, parce qu ils
sont silencieusement mal interpretables : delta vaut toujours web moins design, et un
frameWidth different de viewport rend toute position incomparable. Des tests verifient que
ces explications restent en place.
L ensemble pese environ 8,5 ko, soit a peu pres 2000 tokens, charges une fois par session.
Ligne de commande
bun run src/cli.ts --url http://localhost:3000 --node "4029:12345" --map map.jsonEntree | Role | Defaut |
| Page a mesurer : URL servie ou chemin de fichier HTML | requis |
| Id de frame Figma, deux-points | requis |
| Fichier cible si plusieurs sont connectes | fichier unique |
| Carte calque vers selecteur CSS |
|
| Chemin du rapport |
|
| Port du bridge |
|
| Largeur du viewport | largeur de la frame |
| Hauteur du viewport |
|
| Ajoute la diff pixel, export PNG automatique | desactive |
| Ecarts detailles sur stdout |
|
| Seuil d echec de la diff pixel |
|
Code de sortie 0 si conforme, 1 s il reste des ecarts, 2 si l appel est mal forme.
L id de frame se lit dans l URL Figma (?node-id=4029-12345), en remplacant le tiret par
deux-points, ou via le tool get_selection du bridge.
Quelle page est comparee
Trois facons de la designer, selon ce que tu as sous la main.
Une page deja mise en etat par ton agent (SPA)
Si ton agent pilote un Chrome headless via chrome-devtools-mcp, il a souvent deja fait le
travail : connexion, navigation, ouverture d un panneau, remplissage d un formulaire. Mesurer
dans un navigateur neuf perdrait tout cela. On rejoint donc le sien par CDP :
bun run src/cli.ts --cdp http://localhost:9222 --target /tarifs --node "4029:12345"Sans --url, rien n est navigue : la page est mesuree exactement dans l etat ou elle se trouve.
--target choisit l onglet par fragment d URL quand plusieurs sont ouverts, sinon le premier
onglet non vide est pris. La deconnexion ne ferme pas le navigateur de l agent.
Deux effets de bord assumes, tous deux sur demande explicite seulement : passer --url navigue
et efface l etat, passer --width redimensionne. Par defaut, le viewport de la page est adopte
tel quel et reporte dans viewport ; s il ne correspond pas a la largeur de la frame, la sortie
le signale, parce que comparer des positions a des largeurs differentes ne veut rien dire.
Une URL servie, ou un fichier local
Celle que tu designes, et rien d autre : l outil ne lance aucun serveur. Deux formes sont acceptees.
bun run src/cli.ts --url http://localhost:3000/tarifs --node "4029:12345"
bun run src/cli.ts --url ./dist/tarifs.html --node "4029:12345"Un chemin de fichier est converti en file://, donc une page generee mais pas encore servie
reste mesurable sans monter un serveur pour rien. Un projet avec dev server se mesure sur son
URL, route par route : une frame Figma correspond a une page, donc a un appel.
La page doit etre dans l etat a mesurer au moment de l appel. Pour une SPA, passer l URL profonde de la route, ou preferer le mode attache ci-dessus.
Methode recommandee
Une frame, une route, un appel. Une frame Figma correspond a une page ; ne cherche pas a tout
mesurer d un coup. Pour du responsive, un appel par breakpoint avec --width et la frame
correspondante.
Construis la carte par iterations. Commence par les blocs structurants, sections et composants
principaux, une dizaine d entrees. Lance, lis unmappedLayers, ajoute ce qui compte. Un calque
purement decoratif ou un groupe technique Figma n a pas besoin d entree. La carte n a pas vocation
a etre exhaustive, seulement a couvrir ce qui doit etre juste.
Corrige dans l ordre du rapport. presence, visibilite et selecteur viennent en tete parce
qu ils invalident les mesures en dessous : tant qu un selecteur est mort ou ambigu, les ecarts de ce
calque ne veulent rien dire. Ensuite, si viewport differe de frameWidth, regle la largeur avant
de lire la moindre position. Le reste seulement apres.
Cherche la cause commune, pas les symptomes. Une meme propriete fausse sur plusieurs calques
est presque toujours une seule regle CSS, une variable ou un token. Un gap faux sort accompagne
des elements qu il a decales : corriger le gap fait disparaitre les trois lignes.
Garde --pixel hors de la boucle. Il est plus lent, plus bruyant, et ses zones servent a
trouver ce que la carte ne couvre pas. Un passage en fin de parcours, pour completer la carte, puis
on repasse en comparaison de valeurs.
Versionne map.json avec le code. C est l artefact durable : il survit aux refontes de
structure et documente quel element implemente quel calque. Le rapport, lui, est jetable.
Ne vise pas zero a tout prix. Si un ecart est un choix assume, retire le calque de la carte ou corrige la maquette. Un seuil qu on relache pour faire passer la mesure ne mesure plus rien.
La carte des calques
map.json associe un nom de calque Figma a un selecteur CSS. Voir map.example.json.
Seuls les calques listes sont compares ; les autres remontent dans unmappedLayers a chaque
appel, de quoi completer la carte sans la deviner.
Comment la comparaison fonctionne
Ce n est pas une comparaison d images, et pas non plus un parcours d arbre. Pour chaque entree de la carte, l outil lit les valeurs du calque cote Figma et celles de l element cote DOM, puis compare des nombres et des couleurs. Les positions sont ramenees dans le repere de la frame de chaque cote, jamais comparees de proche en proche.
Consequence importante : la structure de la page n a pas a ressembler a la hierarchie Figma.
Balises differentes, enveloppes supplementaires, grid la ou la maquette a un auto-layout, tout
cela est sans effet tant que le rendu correspond. La carte est le seul point de contact entre les
deux mondes, et c est ce qui rend l outil utilisable sur du code reel plutot que sur du code
genere pour ressembler a la maquette. test/site/refactor.html le demontre : meme rendu, DOM sans
rapport, zero ecart.
La limite de ce choix est symetrique : un calque que la carte ne nomme pas n est pas mesure, et un
calque Figma qui correspond a plusieurs elements de la page ne peut pas etre exprime. C est a cela
que sert --pixel, qui compare en plus un export de la frame au screenshot de la page et signale
les zones divergentes avec l element DOM qui s y trouve.
Pourquoi les deux, et pas seulement la diff pixel
Sur la page de test, la comparaison de valeurs nomme 14 ecarts avec leur valeur cible ; la diff pixel rend un ratio de 0,00236 et six zones. Les deux voient des choses differentes.
La diff pixel attrape ce que la carte ignore : les titres des cartes 2 et 3, absents de la carte,
ressortent comme zones deplacees. Mais elle rate le fond de la carte 2, passe de #ffffff a
#f9fafb, parce que l ecart est sous son seuil ; et elle rate le rayon des bordures pour la meme
raison. Baisser le seuil les ferait apparaitre, au prix du bruit de rendu.
Car c est la le point : entre un export Figma et un rendu navigateur, l iso pixel parfait n existe pas. Le rasterisation des textes, l antialiasing et le placement subpixel different par construction. Il y a donc toujours un seuil, donc toujours une zone d aveuglement en dessous et du bruit au-dessus. Et une zone magenta ne dit ni quelle propriete est fausse, ni quelle valeur atteindre : elle localise, elle n explique pas.
La comparaison de valeurs, elle, atteint le zero exact quand la page est conforme, et sort
padLeft: figma=24 web=16, directement actionnable. Elle ne voit en revanche que ce que la carte
nomme et que la liste de proprietes couvre : ni ombres, ni degrades, ni icones, ni images.
Bref : la diff pixel pour ne rien manquer, la comparaison de valeurs pour savoir quoi corriger.
Ce que la comparaison ne voit pas
Elle lit les valeurs calculees par le navigateur, donc bold, 1.5rem, rgb(37, 99, 235) et un
interligne sans unite sont deja resolus en 700, 24px et #2563eb : aucune de ces ecritures ne
produit de faux ecart. Trois situations la mettraient en defaut si elles n etaient pas traitees, et
elles passent en tete du rapport parce qu elles rendent le reste des mesures douteux : un selecteur
qui ne matche rien (presence), un element present mais masque dont la boite vaut zero
(visibilite), et un selecteur qui matche plusieurs elements dont un seul est mesure (selecteur).
Un angle mort connu reste : getComputedStyle renvoie la police declaree, pas la police
reellement utilisee pour le rendu. Si Inter est declaree mais absente, la comparaison passe
alors que la page rend en fallback. document.fonts.check ne permet pas de le detecter, il
repond vrai pour une famille inexistante. Seule la diff pixel attrape ce cas.
Ce qui est compare, et ce qui ne l est pas
Position et taille relatives a la frame, typographie (taille, graisse, famille, interligne, interlettrage), couleurs de texte et de fond, rayon, paddings, gap, contenu textuel. Tolerances : 2px en position et taille, 1px en espacement, 0.5px en typographie, zero sur la graisse. Une propriete qu une seule des deux sources sait exprimer est ignoree plutot que comptee comme un ecart.
Hors perimetre : les etats (hover, focus, disabled), les pseudo-elements, les ombres et degrades, les breakpoints autres que celui mesure, et tout calque absent de la carte. Pour du responsive, lancer un appel par frame et par largeur.
--pixel recupere en plus un export PNG de la frame via le bridge, le compare au screenshot de
la page, regroupe les pixels divergents en zones et nomme l element DOM sous chacune. Cela
rattrape ce que la carte ne couvre pas, au prix du bruit habituel des diffs pixel.
Une page derriere authentification, ou un etat construit par interaction, se mesure via --cdp
en rejoignant le navigateur de l agent. Le mode autonome, lui, part toujours d une session vierge.
Tests
bun test # 70 tests
bun run typecheckSix niveaux : la logique de comparaison et la lecture de l arbre Figma sans I/O, l extraction DOM dans un vrai Chromium, le mode attache contre un navigateur lance avec un port de debug, le CLI complet contre un faux bridge, le contrat MCP via un vrai client stdio, et un e2e sur un site complet. Le mode attache verifie explicitement que la mesure ne detruit ni l etat construit dans la page ni le navigateur de l agent.
test/site/ contient une landing page, sa maquette de reference et quatre variantes : les ecarts
injectes par defects.css, les memes valeurs ecrites autrement (equivalents.html), une
structure DOM sans rapport avec Figma (refactor.html) et les pieges de mesure
(pieges.html). Le fichier defects.css injecte
huit ecarts de nature differente : element absent, padding, taille de police, graisse, couleur de
texte, couleur de fond, gap, rayon, interlettrage et contenu textuel. Le test verifie que chacun
ressort avec la bonne valeur cible et le bon sens de correction, et surtout que la version
conforme de la meme page ne produit aucun ecart. Un outil qui signale tout ne sert a rien : cette
seconde moitie du test compte autant que la premiere.
This server cannot be deployed
Maintenance
Related MCP Connectors
Score any URL against a real design contract — 42 checks, A-F grade, token + motion validation.
- miromiroOAuthapp.miromiro
Turn any live website into brand colors, fonts, design tokens, SVGs, Lottie and paste-ready code.
- mcpOAuthcom.screenshotink
Screenshot, diff, audit and sitemap-capture any web page — 5 MCP tools for AI agents.
Hosted browser for AI agents: screenshots, post-JS DOM, console, WCAG. No install, no API key.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables AI-powered visual analysis of webpages for UI/UX assessment, including screenshot capture, element detection, accessibility auditing, and comprehensive JSON reporting.1-
- AlicenseNot gradedqualityDmaintenanceVerifies that AI-generated UI code matches Figma design specs by rendering components in a real browser, comparing computed CSS, and returning patch-ready fixes with scored parity reports.MIT
- FlicenseAqualityBmaintenanceCompares Figma frames to live pages, checking colors, fonts, and border radii, and generates a shareable HTML report.6-
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to score live URLs against a 40-check design contract, validate DTCG tokens and Lottie animations, audit accessibility, and retrieve design-system contracts, catalogs, and review rubrics.5 npmMIT