Home Projets Articles Profil AI Studio Me contacter
Design System 7 min de lecture

Piloter Figma par le code : automatiser un design system avec l'API Plugin

L'API Plugin de Figma n'est pas réservée aux créateurs de plugins distribués. C'est l'outil le plus puissant qui existe pour industrialiser un design system directement dans le fichier, sans quitter l'environnement de design.


Illustration abstraite : une architecture de code orange et des panneaux techniques flottants

1. Le design system comme code, même dans Figma

Un design system mature n'est pas une bibliothèque de composants qu'on maintient à la main, variant après variant, couleur après couleur. C'est un système de règles dont la cohérence doit être garantie à l'échelle, non par la discipline humaine, mais par des contraintes structurelles. Dans le code, on y arrive avec des tests, des linters, des pipelines. Dans Figma, on y arrive avec l'API Plugin.

L'API expose la quasi-totalité du modèle objet de Figma : créer des nœuds, naviguer dans le document, lire et écrire des propriétés, binder des variables, accéder aux text styles, manipuler des instances de composants. Le tout en JavaScript asynchrone, dans un contexte sandbox qui tourne directement dans l'application desktop. Résultat : ce qui prendrait des heures de manipulation manuelle s'exécute en quelques secondes.

Sur le design system d'un grand groupe, j'ai utilisé cette API pour générer des dizaines de composants avec leurs variants complets, binder plusieurs centaines de variables de tokens, et auditer automatiquement la conformité de chaque nœud. Voici les patterns concrets qui font la différence.

2. Générer des composants et variants par script

La première valeur de l'API, c'est la génération. Plutôt que de dupliquer manuellement un composant pour créer ses états (Default, Hover, Focus, Disabled, Pressed), on écrit une fonction qui les produit tous en boucle, avec les bonnes propriétés dès le départ.

Le point d'entrée pour instancier un composant existant est createInstance() appelé sur le nœud principal, puis setProperties() pour configurer les variants :

// Récupérer un composant local par son ID
const mainComp = await figma.getNodeByIdAsync('120:45');
const instance = mainComp.createInstance();

// Configurer les propriétés du variant
instance.setProperties({
  'State#node-id': 'Hover',
  'Size#node-id':  'md',
});

La subtilité : les clés de propriété portent un suffixe #nodeId propre à chaque ComponentSet. Il faut lire componentPropertyDefinitions une fois en amont pour récupérer les noms exacts avant d'appeler setProperties, sinon le variant reste silencieusement inchangé. Prendre le temps de cette inspection préalable évite des heures de débogage.

3. Binder des variables et tokens en masse

C'est là que l'API devient vraiment stratégique. Un design system sans token binding n'est pas un design system : c'est une bibliothèque de formes. Chaque couleur, chaque espacement, chaque rayon de bordure doit être lié à une variable pour que le dark mode, les thèmes de marque et la maintenance à grande échelle fonctionnent sans intervention manuelle.

Un composant sans token binding n'est pas un composant de DS, c'est une forme avec de bonnes intentions.

Pour les couleurs, l'API impose un pattern en deux étapes : d'abord récupérer la variable, ensuite l'appliquer via setBoundVariableForPaint qui retourne un nouveau paint object (le tableau fills est read-only, il faut donc cloner, modifier, réassigner) :

const colorVar = await figma.variables.getVariableByIdAsync(
  'VariableID:101:12' // content/primary
);

// fills est read-only, clone obligatoire
const fills = JSON.parse(JSON.stringify(node.fills));
node.fills = [figma.variables.setBoundVariableForPaint(fills[0], 'color', colorVar)];

Pour les espacements et rayons, on utilise setBoundVariable directement sur le nœud, avec le nom de la propriété CSS-like comme clé :

const sp4 = await figma.variables.getVariableByIdAsync(
  'VariableID:204:8' // spacing-4 = 24px
);
node.setBoundVariable('paddingTop',   sp4);
node.setBoundVariable('paddingBottom', sp4);
node.setBoundVariable('itemSpacing', gapVar);
node.setBoundVariable('topLeftRadius', radiusVar);

Pour les text styles, l'API exige également le pattern asynchrone : await node.setTextStyleIdAsync('S:...'). La version synchrone (node.textStyleId =) existe mais ne fonctionne pas de façon fiable selon les contextes. L'await n'est pas optionnel.

4. Auditer la conformité du design system

L'API ne sert pas qu'à créer : elle permet aussi de détecter ce qui est cassé. Sur un DS avec des dizaines de composants et des centaines de nœuds, il est humainement impossible de vérifier à la main que chaque valeur est bien bindée à un token. Un script d'audit le fait en quelques secondes.

Le principe : parcourir récursivement tous les descendants d'un nœud racine, et pour chaque nœud vérifier la présence de bindings sur les propriétés critiques :

const root = await figma.getNodeByIdAsync('NODE_ID');
const issues = [];

for (const n of root.findAll(() => true)) {
  // Fill sans binding
  if (n.fills?.some(f => f.type === 'SOLID' && !n.boundVariables?.fills))
    issues.push({ id: n.id, issue: 'fill non bindé' });

  // Text node sans text style
  if (n.type === 'TEXT' && !n.textStyleId)
    issues.push({ id: n.id, issue: 'text style manquant' });

  // Spacing brut sur un frame
  if (n.type === 'FRAME' && n.paddingLeft > 0 && !n.boundVariables?.paddingLeft)
    issues.push({ id: n.id, issue: 'padding non bindé' });
}

return { total: issues.length, issues };

Le rapport renvoyé liste précisément les nœuds problématiques : plus besoin de naviguer le fichier à l'aveugle. Sur une refonte de composant, ce script m'a permis de détecter et corriger 90+ bindings manquants en un seul passage.

5. Les pièges réels à connaître

L'API est puissante mais intransigeante sur plusieurs points. Ignorer ces règles produit des bugs silencieux difficiles à diagnostiquer :

  • Charger les fonts avant d'écrire du texte. Tout appel modifiant un text node doit être précédé de await figma.loadFontAsync({ family, style }), même si la font est déjà présente dans le fichier. Sans ça, l'opération échoue sans erreur visible.
  • FILL après appendChild, jamais avant. Définir layoutSizingHorizontal = 'FILL' avant d'appeler parent.appendChild(child) n'a aucun effet. L'ordre est impératif : append d'abord, puis sizing.
  • Les paints sont read-only, toujours cloner. Modifier directement node.fills[0].color lance une erreur. Il faut JSON.parse(JSON.stringify(node.fills)), modifier le clone, puis réassigner.
  • Les couleurs sont en range 0-1, pas 0-255. Rouge pur = { r: 1, g: 0, b: 0 }. Une couleur hex comme #FF4D00 se convertit en { r: 1, g: 0.302, b: 0 }.
  • Retourner les IDs créés dans chaque script. Le contexte de page se réinitialise entre les appels : sans return { createdNodeIds: [...] }, tracer ce qui a été créé devient impossible.
  • Ne jamais enchaîner sur appendChild. parent.appendChild(child) renvoie undefined, pas le nœud. Enchaîner dessus lance un TypeError.

6. Le gain concret : cohérence et vitesse à l'échelle

Mettre en place ces scripts représente un investissement initial non négligeable. Comprendre le modèle objet de Figma, écrire les patterns de binding, construire les fonctions d'audit demande du temps. Ce temps est récupéré dès la première utilisation à grande échelle.

Sur le design system, générer manuellement un composant avec ses six états, ses trois tailles et ses quatre variantes d'apparence représentait environ deux heures de travail minutieux, avec un risque d'oubli ou d'incohérence à chaque duplication. Par script, la même opération prend moins de trente secondes et produit un résultat identique, token par token, à chaque exécution.

L'autre bénéfice, moins évident mais tout aussi réel : la documentation vivante. Quand la structure d'un composant est décrite dans du code, elle peut être versionnée, relue, auditée par un pair qui ne connaît pas Figma. La frontière entre design system et design token pipeline s'efface. Le DS devient un artefact de code autant que de design.

Conclusion

L'API Plugin de Figma est l'outil qui transforme un design system de taille humaine en infrastructure maintenable. Elle ne remplace pas le jugement du designer, elle libère ce jugement des tâches répétitives et réductibles à des règles. Générer des variants, binder des tokens, auditer la conformité : ce sont des opérations déterministes qui n'ont aucune raison d'être faites à la main.

Le DS n'est plus une bibliothèque que l'on garde à jour à la main. Il devient un système piloté par des règles explicites, dont la cohérence est vérifiable et reproductible. C'est exactement ce que les équipes produit attendent d'un design system sérieux.