Que fait Koobiq Icons Figma Audit ?
Cette compétence vérifie qu'un composant d'icône dans le fichier Figma des icônes Koobiq suit les conventions structurelles de la bibliothèque, afin que figma:sync (la pipeline @figma-export) produise un SVG propre et correct. Détecter les problèmes dans Figma est bien moins coûteux que de les découvrir après l'exportation, car l'exportation transforme silencieusement tout ce qu'il trouve — un nom de calque erroné, un nœud supplémentaire ou une coordonnée hors grille devient un SVG cassé sans erreur.
Il s'agit d'un audit en lecture seule par défaut : inspectez, puis rapportez une liste de contrôle claire réussite/échec avec des constatations spécifiques et exploitables. Ne modifiez le fichier Figma que si l'utilisateur vous demande explicitement de corriger quelque chose (voir « Proposer des corrections »).
Prérequis
-
Le MCP Figma doit être connecté. Cette compétence l'utilise via
get_metadata(aperçu de la structure),use_figma(inspection précise via l'API du Plugin) etget_screenshot(vérification visuelle de cohérence). -
Avant tout appel
use_figma, chargez la compétencefigma-use— elle documente les règles de l'API du Plugin et évite les échecs courants et difficiles à déboguer. -
La clé du fichier et les pages de taille sont fixes (vérifiées par rapport au document en direct). Pour sauter un aller-retour de découverte de page, accédez directement à une page par son identifiant de nœud :
taille identifiant du nœud de la page 16 1320:1738724 42149:86032 42149:86248 42149:86164 42149:863Clé du fichier :
WvUarBj8tO256vML4PkBLl. Chaque nom de page est le suffixe de taille ; une icône nomméefoo_24se trouve sur la page24. Document : https://www.figma.com/design/WvUarBj8tO256vML4PkBLl/%F0%9F%9F%A5-koobiq-%C2%B7-icons?node-id=1320-17387- Cette clé de fichier est la source de vérité — elle correspond à
config.mjs. Le lien « Draw an icon » danspackages/icons/README.mdpointe vers un fichier différent et erroné — ignorez-le et faites confiance à la clé ci-dessus. get_metadatasansnodeIdliste uniquement la première page (ICONS, qui ne contient aucun composant d'icône) — elle n'énumère pas les pages de taille. Ne l'utilisez pas pour la découverte de pages ; utilisez les identifiants de nœud ci-dessus.
- Cette clé de fichier est la source de vérité — elle correspond à
Si le fichier est inaccessible
Si un appel Figma échoue — erreur d'authentification/de permission, « le nœud n'existe pas », un fichier vide/inattendu, ou le MCP ne répond pas — arrêtez-vous et signalez-le à l'utilisateur au lieu de deviner. Dites-lui ce qui a échoué et demandez-lui de :
- fournir un lien Figma différent / correct (la clé du fichier dans
config.mjspeut être obsolète, ou il a collé un lien vers un autre fichier ou une branche), ou - revérifier le
FIGMA_TOKENdans le.envdu dépôt (le jeton d'accèsfigma-export/ MCP peut être manquant, expiré ou révoqué), ou - confirmer que son compte Figma a accès au fichier (il s'agit d'un fichier de conception privé — il peut avoir besoin d'être invité ou d'utiliser un jeton lié à un compte disposant des autorisations).
N'inventez pas un résultat d'audit à partir d'hypothèses ; une vérification n'a de sens que par rapport au fichier en direct.
Lectures parallèles et contexte de page
Les appels de lecture Figma peuvent être exécutés en parallèle, mais seulement avant la première modification Figma de la tâche. Après le début d'une modification, exécutez les appels Figma un par un.
Les lectures parallèles qui changent de page peuvent interférer les unes avec les autres. Lors d'une exécution observée, une lecture ciblant la page 24 a renvoyé les données de la page 16 (le même nombre de composants, des noms erronés), et l'audit a signalé les variantes 24px comme manquantes. Protégez chaque lecture qui change de page :
// À utiliser dans chaque lecture qui change de page. Renvoie les données uniquement pour les nœuds appartenant à PAGE_ID.
const PAGE_ID = "42149:860"; // identifiant du nœud de la page du tableau ci-dessus
const NAMES = ["foo_24"]; // noms des composants à rechercher
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("inadéquation du contexte de page avant la lecture");
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const hits = figma.currentPage
.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => NAMES.includes(n.name) && pageOf(n) && pageOf(n).id === PAGE_ID)
.map((n) => ({ name: n.name, id: n.id, w: n.width, h: n.height }));
if (figma.currentPage.id !== PAGE_ID) throw new Error("le contexte de la page a changé pendant la lecture");
return { pageId: PAGE_ID, hits };
Si une garde lève une exception, rejetez ce résultat et réexécutez la lecture. Une affirmation de « manquant » (par exemple, « aucune variante 24px ») doit reposer sur une lecture protégée de cette page, ou sur une recherche par identifiant de nœud. Ne signalez jamais l'absence à partir d'une lecture qui a levé une exception ou d'une page que vous n'avez pas vérifiée.
Étape 1 — Résoudre la cible
L'utilisateur peut vous donner l'un des éléments suivants :
- Une URL ou un identifiant de nœud Figma (par exemple
node-id=44731-6643) → inspectez exactement ce nœud, puis recherchez également ses variantes de taille sœurs par nom pour la vérification croisée des variantes. - Un nom d'icône (par exemple
floating-window, oufloating-window_16) → trouvez chaque variante de taille sur les pages d'icônes et vérifiez-les toutes. Les nouvelles icônes sont souvent non publiées et peuvent même être sans nom, donc si une correspondance de nom échoue, listez les composants sur la ou les pages pertinentes et demandez à l'utilisateur de pointer le nœud, ou inspectez par identifiant de nœud.
Les noms de page sont égaux au suffixe de taille. Balayage des tailles : vérifiez les cinq pages (16 / 24 / 32 / 48 / 64) pour chaque nom demandé, car une icône peut exister dans des tailles que l'utilisateur n'a pas mentionnées. Utilisez les identifiants de nœud de page du tableau Prérequis, un appel use_figma par page, chacun avec la garde de « Lectures parallèles et contexte de page ». Exécutez-les en parallèle uniquement avant toute modification Figma. Signalez le résultat sous forme de tableau (nom × taille → identifiant de nœud ou manquant).
Étape 2 — Inspecter
Exécutez ce script en lecture seule (adaptez les cibles/l'identifiant de page). Il renvoie tout ce dont les règles ci-dessous ont besoin. Associez-le à une get_screenshot pour pouvoir confirmer que le glyphe s'affiche réellement.
// Collez dans use_figma (skillNames: "figma-use"). Définissez PAGE_ID et NAMES.
// Identifiants des nœuds de page : 16=1320:17387 24=42149:860 32=42149:862 48=42149:861 64=42149:863
const PAGE_ID = "1320:17387"; // page dont le nom est la taille, par ex. "16"
const NAMES = ["floating-window_16"]; // nom(s) de composant(s) à auditer sur cette page
figma.skipInvisibleInstanceChildren = false; // false : la règle G doit également voir les calques cachés
const page = await figma.getNodeByIdAsync(PAGE_ID);
await figma.setCurrentPageAsync(page);
if (figma.currentPage.id !== PAGE_ID) throw new Error("inadéquation du contexte de page avant la lecture");
const toHex = (c) => {
const h = (n) => Math.round(n * 255).toString(16).padStart(2, "0").toUpperCase();
return "#" + h(c.r) + h(c.g) + h(c.b);
};
const describeFill = (n) => {
if (!n.fills || n.fills === figma.mixed) return n.fills === figma.mixed ? "MIXED" : null;
return n.fills.filter((f) => f.visible !== false).map((f) => ({
type: f.type,
hex: f.type === "SOLID" ? toHex(f.color) : undefined,
opacity: f.opacity,
boundVar: f.boundVariables && f.boundVariables.color ? f.boundVariables.color.id : null,
}));
};
const pageOf = (n) => { let p = n; while (p && p.type !== "PAGE") p = p.parent; return p; };
const all = page.findAllWithCriteria({ types: ["COMPONENT", "COMPONENT_SET"] })
.filter((n) => pageOf(n) && pageOf(n).id === PAGE_ID);
const report = [];
for (const name of NAMES) {
const c = all.find((x) => x.name === name);
if (!c) { report.push({ name, found: false }); continue; }
report.push({
name: c.name, id: c.id, type: c.type,
width: c.width, height: c.height, x: c.x, y: c.y,
layoutMode: c.layoutMode,
clipsContent: c.clipsContent,
description: c.description,
children: c.children.map((ch) => ({
name: ch.name, type: ch.type,
w: ch.width, h: ch.height, x: ch.x, y: ch.y,
constraints: ch.constraints,
fills: describeFill(ch),
styles: { fill: ch.fillStyleId, stroke: ch.strokeStyleId, effect: ch.effectStyleId },
boundVariableFields: ch.boundVariables ? Object.keys(ch.boundVariables) : [],
})),
});
}
if (figma.currentPage.id !== PAGE_ID) throw new Error("le contexte de la page a changé pendant la lecture");
return report;
Pour valider le contrat de couleur de manière fiable, lisez également une icône sœur de bonne qualité de la même taille (par exemple list_16) de la même manière et comparez la clé de la variable liée de la forme primaire (Règle D). La couleur hexadécimale nominale du contrat et la valeur réellement stockée dans le fichier diffèrent (voir Règle D), donc une comparaison avec un voisin réel est plus fiable que la correspondance avec un hexadécimal littéral.
Étape 3 — Vérifier par rapport aux règles
Signalez chaque élément comme ✅ réussi / ❌ échoué avec la valeur observée et, en cas d'échec, ce qu'elle devrait être.
A. Type de nœud et nom
-
Le nœud est un
COMPONENT(ou unCOMPONENT_SETde variantes de taille), pas unFRAME,GROUPouINSTANCE. Seuls les composants s'exportent. -
Premier caractère — vérifiez ceci en premier, cela change la signification du nom :
- Commence par
.ou_→ un composant interne, intentionnellement NON exporté. LefilterComponentde l'export dans.figmaexportrc.mjssupprime tout nom correspondant à^[._]. C'est par conception, pas un bug — ne l'auditez pas comme une icône cassée. Signalez-le uniquement si l'utilisateur s'attendait à ce que ce nœud soit exporté (la correction est alors de le renommer sans le./_initial). - Commence par
-→ invalide, pas autorisé. Signalez comme un échec. - Commence par un chiffre → autorisé mais déconseillé. Il s'exporte quand même (par exemple
3-columns_32), mais un nom devrait de préférence commencer par une lettre anglaise — suggérez à l'utilisateur de le renommer. Recommandation souple, pas un blocage. - Commence par une lettre minuscule anglaise → le cas préféré.
- Commence par
-
Format du nom — un nom d'icône exportable doit correspondre à cette expression régulière :
^[a-z0-9]+(-[a-z0-9]+)*_(16|24|32|48|64)$Uniquement lettres minuscules anglaises et chiffres ; mots reliés par des traits d'union simples (pas de
--, pas de trait d'union en tête/queue) ; puis exactement un trait de soulignement ; puis un suffixe de taille valide. Pas de majuscules, d'espaces, de cyrillique/autres écritures, ni d'autres signes de ponctuation.- ✅
floating-window_16,api_24,arrow-up-to-bracket_16(et, format valide mais chiffre déconseillé,3-columns_32) - ❌
Floating-Window_16(majuscule) ·плавающее-окно_16(pas anglais) ·floating_window_16/icon__16(deux traits de soulignement) ·floating--window_16(double trait d'union) ·floating-window-16(trait d'union au lieu de trait de soulignement avant la taille) ·-floating-window_16(trait d'union en tête) ·floating-window_20(taille non dans l'ensemble autorisé) ·floating-window_16(espace de fin)
- ✅
-
Suffixe
-o= variante contour. Le nom sans-oest la variante remplie. Une paire telle quecloud_24/cloud-o_24oucloud-arrow-up_24/cloud-arrow-up-o_24est une icône en deux styles. C'est valide et ce n'est pas un conflit ou un doublon ; ne demandez pas de renommage à cause de cela. -
Le suffixe numérique doit être égal à la taille réelle du composant (Règle B). Un
..._16qui fait 24×24 est un bug.
B. Géométrie et mise en page
- Pas de mise en page automatique :
layoutMode === "NONE". La mise en page automatique réorganise les enfants et corrompt l'exportation. - Fixe et carré :
width === height === size. La taille doit être un nombre entier de pixels (aligné sur la grille de pixels) ; les tailles de composant fractionnaires signifient que l'icône n'a pas été dessinée sur la grille. - Clip content DÉSACTIVÉ :
clipsContent === false. Le bouton « Clip content » doit être désactivé. Il doit être le même (désactivé) pour chaque variante de taille de l'icône. Lorsqu'il est activé, toute illustration dépassant les limites du cadre est silencieusement rognée lors de l'exportation, produisant une icône coupée ou tronquée.
C. Structure racine (c'est là que se cachent la plupart des bugs réels)
- Icône monochrome : exactement un enfant racine — un
VECTORnomméshape. - Icône bicolore : exactement deux enfants racines —
shape(primaire) etshape-2(secondaire). Rien d'autre. - Aucun autre nœud à la racine du composant : pas de trames, groupes, rectangles, tranches, vecteurs en double ou calques d'aide cachés. Chaque nœud supplémentaire se retrouve dans le SVG exporté.
- Les contraintes de chaque forme sont
SCALEsur les deux axes (horizontal: "SCALE",vertical: "SCALE") afin que l'illustration soit mise à l'échelle avec le cadre.- Gravité : avertissement (hygiène), pas un blocage d'exportation. Les contraintes contrôlent la façon dont le composant se redimensionne dans Figma, pas les données de chemin exportées. Corrigez-les avec les autres modifications Figma approuvées, car les instances et le redimensionnement en dépendent.
- Idéalement, chaque forme est un chemin aplati unique (voir les étapes « Draw an icon » du README du dépôt : contour, aplatir, règle de remplissage Non-zero). Signalez plusieurs sous-chemins ou groupes non aplatis comme un avertissement.
D. Contrat de couleur de remplissage
Le contrat (README du dépôt « SVG Color Zones Guide ») :
- Zone primaire
shape→ noir#21222C. L'exportation supprime sonfillafin que le SVG hérite decurrentColor. - Zone secondaire
shape-2→ rouge#E21D03. L'exportation le réécrit enfill="currentColor".
Réalité pratique : dans le fichier de production, le remplissage primaire est lié à la variable partagée Icon/Contrast, qui se résout en #000819 avec une opacité de peinture de 0,85 — pas un #21222C littéral. Donc :
- Clé de référence :
91eb54e0eae961c540c994192c4eed18e4ea1825—Icon/Contrastde🟥 koobiq · components 19(clé de collectionc78fab428cf2003c9aa53d86b132a2fa95acb2e2). - Comparez la clé de la variable liée de la peinture primaire avec cette référence (
(await figma.variables.getVariableByIdAsync(boundId)).key), et vérifiez que l'opacité de la peinture est de 0,85. Ne comparez pas les noms :Icon/Contrastexiste dans plusieurs bibliothèques et collections. La seule variable autorisée dans une icône est celle-ci, par sa clé. Une variable du même nom avec une clé différente (d'une autre bibliothèque ou collection) est un échec, pas un avertissement. Correction : reliez à la variable de clé de référence de la même collection de bibliothèque. - La
shape-2secondaire doit être soit un#E21D03littéral solide (l'exportation le fait correspondre comme un hexadécimal littéral —secondaryZoneColorHexdans.figmaexportrc.mjs), soit liée par clé àIcon/Errorde🟥 koobiq · components 19(clée736157dcefc2a3e016ec2ddd41a7dc8cf8a5bc6). Pour le cas de la variable, l'opacité de la peinture n'est pas vérifiée. Tout autre remplissage échoue. - Un remplissage primaire qui est une couleur littérale simple ne correspondant à aucune icône sœur, ou un remplissage secondaire qui n'est pas
#E21D03, sera exporté avec la valeur hexadécimale littérale, ne respectant pas le contratcurrentColor— signalez-le comme un échec.
E. Cohérence entre les variantes de taille
Lisibilité : toutes les variantes de la même icône (foo_16, foo_24, foo_32, foo_48, foo_64) doivent avoir :
- Le même nombre d'enfants racine (tous monochromes ou tous bicolores — pas de mélange).
- Les mêmes noms d'enfants (tous
shape/shape+shape-2). - Chaque forme a
clipsContent === false(signalé dans la règle B, mais vérifié de manière homogène entre les tailles). - S'il s'agit d'un
COMPONENT_SET, le nom du composant pour une taille donnée doit correspondre au nom de l'ensemble (foo) avec le suffixe de taille (par exemplefoo_24). Les noms des composants frères peuvent différer des noms des pages — appliquez le tableau de correspondance explicite du fichier pour vérifier que la page24contientfoo_24.
Densité visuelle (avertissement) : parmi les variantes de la même icône, il est inhabituel que le glyphe se réduise à moins de ~55 % de la taille du cadre. Une icône 64px dont le glyphe ne mesure que 24 px de large signale soit un glyphe trop petit, soit une requête pour une taille de cadre inférieure. Ne bloquez pas l'exportation, mais suggérez une vérification de conception.
F. Métadonnées de description
- La
descriptiondu composant (propriétédescriptiondans le panneau Design → Description) doit être non vide, en anglais, expliquer ce que représente le glyphe, et ne pas être la valeur par défaut (par exemple « Description de l'icône », vide). Une description n'est pas nécessaire sur les composants internes (nom commençant par./_). - Chaque variante de taille de la même icône doit décrire le même concept. Par exemple, si
foo_16dit « Fenêtre flottante »,foo_24ne doit pas dire « widget ». Les divergences dans la description seront signalées comme un échec.
G. Cas particulier — icônes avec des calques cachés
Lorsque la règle C s'applique, mais que la forme secondaire n'est pas un enfant racine séparé ; il s'agit plutôt d'une BOOLEAN_OPERATION avec isMask ou un calque supplémentaire avec visible === false (par exemple, un calque de marque-page de conception). Ce n'est pas une violation de la règle structurelle si le calque caché est intentionnel (par exemple, calque de réserve pour les futures générations d'icônes).
- Règle : invisible, pas placé. N'importe quel calque caché (
visible === false) qui n'est nishapenishape-2est autorisé tant qu'il a un nom raisonnable avec un préfixe_(par exemple_old-arrow). Un calque visible supplémentaire sans nom explicite est toujours un échec. - Exception : un calque
_(tiret bas) est un calque de travail temporaire tout à fait normal queskipInvisibleInstanceChildrenpeut signaler ; ignorez-le lors de l'audit.
H. Exportabilité (vérification finale)
Avant de donner le feu vert pour une exportation (par exemple, pour la commande figma:export), vérifiez que l'icône est publiée. Figma n'exporte que les composants qui ont été explicitement marqués comme publiés dans le panneau Assets. Pour vérifier l'état de publication :
// Vérifiez l'état de publication pour chaque composant audité
const publishInfo = component.remoteData?.publishStatus; // 'CURRENT', 'UNPUBLISHED', 'EDITED', 'UNKNOWN'
Tout statut autre que CURRENT ou EDITED signifie que la dernière version du composant n'est pas disponible pour les utilisateurs du fichier de bibliothèque (si les exportations se font à partir d'une bibliothèque Figma, pas seulement à partir du fichier directement). Signalez UNPUBLISHED comme un avertissement. Un composant EDITED a été mis à jour mais pas republié — informez l'utilisateur qu'une republication est nécessaire avant que l'exportation ne voie la dernière version.
Proposer des corrections
Par défaut, rapportez uniquement l'audit. L'utilisateur peut dire « corrige ça » ou « répare-le » ; alors seulement, modifiez les nœuds Figma via use_figma avec les mêmes gardes. Les scripts de correction autorisés sont :
- Renommer un composant (
component.name = newName). - Définir
component.clipsContent = false. - Supprimer les calques superflus (uniquement ceux qui ne sont pas
shape/shape-2). - Renommer un calque en
shapeoushape-2(vérifiez qu'il devient le calque correct). - Définir les contraintes
constraints: { horizontal: "SCALE", vertical: "SCALE" }. - Modifier la
descriptiond'un composant. - Définir les remplissages et les lier (via
setBoundVariableAsync).
N'effectuez jamais de corrections de structure plus profondes (par exemple, vectoriser, décomposer, aplatir). Ces corrections doivent être effectuées par un designer dans l'application Figma. Si l'utilisateur vous demande de réparer de telles structures, dites-lui qu'il ne peut pas les effectuer par programmation et expliquez-lui la solution manuelle.
Rappelez-vous : chaque modification Figma, en particulier la modification des contraintes, reprogramme la façon dont l'icône se redimensionne. Vérifiez avec get_screenshot après la modification et réexécutez une inspection complète.