Figma Variables vs. Code-Tokens: Wo die Synchronisation bricht
Jedes Mal, wenn ich in einem Kickoff-Meeting höre “wir haben Figma Variables, das synct doch automatisch mit dem Code”, korrigiere ich das sofort. Es synct nicht automatisch. Es hat noch nie automatisch gesynct, und es wird mit hoher Wahrscheinlichkeit auch die nächsten Jahre nicht automatisch syncen — nicht, weil Figma das technisch nicht könnte, sondern weil “Design Token” in Figma und “Design Token” im Code zwei unterschiedliche Datenmodelle beschreiben, die zufällig ähnliche Namen tragen. Diese Verwechslung kostet Teams, mit denen ich gearbeitet habe, real Zeit — meistens in Form von Farbwerten, die drei Sprints lang leicht auseinanderdriften, bis jemand den Unterschied bemerkt und ihn manuell wieder zusammenführt.
Ich habe in den letzten Jahren an Design-Token-Pipelines bei DefShop, Oetker Digital und A Eins Digital Innovation gearbeitet, und aktuell baue ich das AI-First Design System, in dem Tokens per KI-Workflow zwischen Figma und Tailwind-Code übersetzt werden. Der Punkt, der sich durch alle diese Projekte zieht: Die Synchronisation zwischen Figma Variables und Code-Tokens ist kein Infrastruktur-Problem, das man einmal löst und dann vergisst. Es ist eine dauerhafte Übersetzungsaufgabe, die an vier ziemlich genau lokalisierbaren Stellen bricht — und wer diese vier Stellen kennt, kann die Pipeline so bauen, dass die Brüche kontrolliert statt chaotisch passieren.
Was Figma Variables eigentlich sind
Figma Variables (seit 2023 verfügbar, davor gab es nur “Styles”, die noch primitiver waren) sind ein internes Datenmodell mit vier Typen: Color, Number, String, Boolean. Jede Variable lebt in einer Collection, kann mehrere Modes haben (Light/Dark, Brand A/Brand B, Density Compact/Comfortable), und kann an praktisch jede Eigenschaft eines Nodes gebunden werden — Fill-Farbe, Corner-Radius, Spacing in Auto-Layout, sogar Textinhalt oder Boolean-Sichtbarkeit einer Layer-Gruppe.
Das klingt strukturell fast identisch zu CSS Custom Properties, und genau diese Ähnlichkeit ist die Falle. Eine Figma Color Variable speichert intern einen RGBA-Wert im sRGB-Farbraum, mit optionaler Referenz auf eine andere Variable (Aliasing — button/primary referenziert color/blue/600). Eine CSS Custom Property speichert einen String, der beliebige gültige CSS-Syntax enthalten kann — oklch(0.55 0.18 250), color-mix(in oklab, red, blue), sogar einen leeren Wert mit Fallback. Figma kennt keine Farbfunktionen, keine berechneten Werte, keine Media-Query-Konditionalität außerhalb der Mode-Struktur. Es ist ein geschlossenes, striktes System — genau richtig für ein Design-Tool, das visuelle Konsistenz innerhalb einer Datei garantieren will, aber strukturell ärmer als das, was moderne CSS kann.
Umgekehrt gilt das Gleiche: CSS Custom Properties kennen keine Modes im Figma-Sinn. Ein Light/Dark-Wechsel in CSS läuft über prefers-color-scheme oder einen Data-Attribut-Selektor (:root[data-theme="dark"]), nicht über eine native “Mode”-Eigenschaft der Variable selbst. Das ist keine Einschränkung von CSS — es ist einfach ein anderes Konzept: Figma bindet die Mode-Auswahl an die Variable, CSS bindet sie an den Kontext, in dem die Variable aufgelöst wird.
Bruchstelle eins: Die Namenskonvention ist keine Spezifikation
Der naheliegendste Ansatz, den fast jedes Team zuerst versucht: Figma-Variable-Namen 1:1 auf CSS-Variable-Namen abbilden. color/primary/600 wird --color-primary-600. Das funktioniert — bis zu dem Moment, in dem zwei unterschiedliche Personen (ein Designer in Figma, ein Entwickler im Code) unabhängig voneinander eine neue Variable anlegen und dabei minimal unterschiedliche Konventionen wählen. color/primary/600 vs. colors/primary-600 vs. Primary/600. Figma erzwingt keine globale Namenskonvention — jede Collection kann ihre eigene Struktur haben, und Figma warnt nicht, wenn zwei Collections inkonsistent benannt sind.
Das ist kein Figma-Bug, es ist eine bewusste Designentscheidung: Figma Variables sind primär für Designer gebaut, die innerhalb einer Datei arbeiten, nicht für eine bidirektionale Code-Synchronisation. Die Namenskonvention, die man braucht, damit ein Skript zuverlässig zwischen Figma und Code übersetzen kann, muss man sich selbst auferlegen — Figma gibt sie einem nicht vor. In der Praxis heißt das: Ich definiere die Namenskonvention einmal explizit als Dokument (nicht nur als stillschweigende Konvention), bevor überhaupt eine Variable angelegt wird, und jede neue Variable durchläuft eine kurze Review, ob sie der Konvention folgt — genau wie ein Code-Linter, nur manuell, weil es dafür keinen automatischen Linter in Figma gibt.
Die Regel, die sich bei mir bewährt hat: Slash-getrennte Pfade in Figma (color/primary/600) übersetzen sich mechanisch in kebab-case mit Bindestrich statt Slash (--color-primary-600). Keine Sonderfälle, keine Abkürzungen, keine Ausnahmen für “das ist doch offensichtlich”. Sobald eine Ausnahme existiert, muss sie dokumentiert und im Übersetzungsskript explizit behandelt werden — und jede zusätzliche Ausnahme ist ein Punkt, an dem die Pipeline später bricht, ohne dass es jemand merkt, bis ein Farbwert im Produkt falsch aussieht.
Bruchstelle zwei: Der Sync ist unidirektional, auch wenn er bidirektional aussieht
Figmas Dev Mode zeigt Variable-Werte im Inspect-Panel an — das erzeugt den Eindruck einer Live-Verbindung. Ist sie nicht. Was tatsächlich passiert: Figma exportiert Variablen über die REST API oder das Plugin-API als JSON-Snapshot zu einem bestimmten Zeitpunkt. Dieser Snapshot muss von einem externen Prozess (Plugin, CI-Job, KI-Workflow) gelesen und in Code-Tokens übersetzt werden. Ändert sich die Figma-Variable danach, weiß der Code nichts davon, bis der Export erneut läuft.
Noch wichtiger: Es gibt keinen offiziellen, nativen Rückkanal. Wenn ein Entwickler einen Token-Wert im Code ändert — sagen wir, weil ein A/B-Test eine andere Primärfarbe testet — fließt das nicht automatisch zurück nach Figma. Die Figma-Datei bleibt auf dem alten Wert stehen, bis jemand manuell nachzieht. Ich habe das in der Praxis mehrfach erlebt: Ein Entwickler passt einen Spacing-Wert im Code an, weil er auf einem bestimmten Breakpoint visuell nicht funktioniert, vergisst (oder weiß nicht), dass er das auch in Figma nachpflegen müsste, und drei Wochen später öffnet ein Designer die Figma-Datei, um eine neue Komponente zu bauen, und arbeitet mit einem Wert, der im echten Produkt gar nicht mehr existiert.
Die Konsequenz, die ich daraus ziehe: Man muss sich für eine Source of Truth entscheiden — nicht als Lippenbekenntnis, sondern als tatsächlichen technischen Zwang. In den Projekten, in denen ich das sauber gelöst habe, ist Code die Source of Truth für Werte, Figma die Source of Truth für Struktur und Benennung. Das bedeutet konkret: Neue Tokens werden zuerst in Figma angelegt (weil Figma die visuelle Iteration erlaubt, die Design braucht), aber sobald ein Token “final” ist und in den Code übernommen wird, wird jede weitere Wertänderung zuerst im Code gemacht und per Skript zurück nach Figma synchronisiert — nicht umgekehrt. Diese Richtung widerspricht der intuitiven Erwartung (man denkt, Design bestimmt die Werte), ist aber praktisch notwendig, weil Code-Änderungen versioniert, reviewt und getestet werden, während Figma-Änderungen das nicht automatisch sind.
Bruchstelle drei: Composite Tokens, die Figma gar nicht abbilden kann
Der subtilste Bruch, und der, der am meisten Frustration verursacht: Figma Variables sind atomar. Eine Variable hat einen Typ und einen Wert. Was Figma nicht kennt, sind zusammengesetzte Tokens — ein “Elevation”-Token, der gleichzeitig box-shadow, border-color und background-blend-mode definiert, oder ein “Typography”-Token, der Font-Size, Line-Height, Letter-Spacing und Font-Weight als eine Einheit bündelt.
Figma löst das über Text Styles (die eine ähnliche, aber getrennte Struktur zu Variables sind) und Effect Styles für Shadows — beide sind eigene Objekttypen, keine Variables, und beide lassen sich nur eingeschränkt mit Variables verknüpfen (aktuell kann man z. B. die Farbe eines Effect Styles an eine Color Variable binden, aber nicht die Blur-Radius- oder Spread-Werte). Im Code dagegen ist ein zusammengesetzter Token oft genau das, was man will: Ein --shadow-elevated-Wert, der eine komplette box-shadow-Deklaration mit mehreren Layern enthält, oder ein CSS-Custom-Property-Set, das über @property sogar typisiert und interpoliert werden kann.
Das bedeutet: Eine 1:1-Automatisierung zwischen Figma und Code stößt hier an eine echte konzeptionelle Grenze, nicht nur an eine Tooling-Lücke. Man kann das teilweise umgehen, indem man in Figma mehrere atomare Variablen definiert (shadow/elevated/blur, shadow/elevated/spread, shadow/elevated/color) und sie im Übersetzungsskript wieder zu einem zusammengesetzten Code-Token zusammenführt — aber das erfordert, dass jemand diese Zusammenführungslogik explizit schreibt und pflegt. Es gibt keinen generischen Mechanismus, der das automatisch für beliebige Composite Tokens erledigt. Genau hier setzen KI-gestützte Übersetzungs-Workflows sinnvoll an: Ein LLM, das die Namenskonvention und die Zusammengehörigkeit von shadow/elevated/*-Variablen versteht, kann diese Zusammenführung robuster leisten als ein starres Mapping-Skript, weil es Muster erkennt statt nur Strings zu ersetzen — aber auch das braucht eine Review-Schleife, kein blindes Vertrauen in die Ausgabe.
Bruchstelle vier: Referenz-Aliasing bricht bei tiefer Verschachtelung
Figma erlaubt, dass eine Variable eine andere Variable referenziert (Alias) — button/background zeigt auf color/primary/600, die wiederum auf gar nichts weiter zeigt (ein “Primitive”-Token). Das ist strukturell identisch zu CSS Custom Properties, die andere Custom Properties referenzieren (--button-bg: var(--color-primary-600)), und in der Theorie überträgt sich diese Verschachtelung sauber.
In der Praxis erlaubt Figma aber nur Referenzen innerhalb derselben oder zwischen explizit verbundenen Libraries, und die Tiefe der Verschachtelung wird in der API-Antwort nicht immer eindeutig aufgelöst — wenn A auf B zeigt und B auf C, liefert die REST API teilweise nur die direkte Referenz (A → B), nicht die vollständig aufgelöste Kette bis C. Ein naives Übersetzungsskript, das nur eine Ebene tief auflöst, produziert dann einen Code-Token, der auf eine Zwischenvariable zeigt, die selbst wieder eine Referenz ist — was in CSS zwar technisch funktioniert (CSS löst var()-Ketten selbst auf), aber die Übersicht zerstört, sobald man versucht nachzuvollziehen, welcher Primitive-Wert tatsächlich am Ende einer langen Alias-Kette steht.
Ich beschränke deshalb in der Praxis die Aliasing-Tiefe bewusst auf maximal zwei Ebenen — Primitive-Token (color/blue/600) und Semantic-Token (color/primary), der auf das Primitive zeigt. Eine dritte Ebene (button/background → color/primary → color/blue/600) sieht zunächst wie saubere Abstraktion aus, macht aber sowohl die Figma-Datei als auch das Übersetzungsskript unnötig komplex, für einen Nutzen, der sich in der Praxis selten auszahlt. Component-spezifische Namen wie button/background binde ich stattdessen direkt an Code-Ebene über Komponenten-Props oder Tailwind-Klassen (bg-primary auf dem Button), statt eine dritte Figma-Variable-Ebene dafür zu bauen, die nur repliziert, was CVA oder die Tailwind-Klasse im Code sowieso schon ausdrückt.
Der pragmatische Workflow, der bei mir funktioniert
Aus diesen vier Bruchstellen ergibt sich der Workflow, den ich aktuell im AI-First Design System nutze: Figma bleibt der Ort, an dem neue Tokens visuell exploriert und benannt werden — dafür ist es das bessere Werkzeug, weil man Farbabstufungen und Spacing-Skalen direkt am Interface sieht, statt sie blind in einer JSON-Datei zu tippen. Sobald ein Token-Set stabil ist, exportiere ich es über die Figma REST API als JSON, lasse ein Skript (aktuell mit LLM-Unterstützung für die Namenskonvention-Normalisierung) daraus @theme-Definitionen in CSS generieren, und committe das Ergebnis wie jeden anderen Code-Change — mit Review, nicht als automatischen, ungeprüften Merge.
Der Rückweg — Code-Änderung zurück nach Figma — läuft bewusst seltener und manueller, meistens als wöchentlicher Abgleich statt als Echtzeit-Sync. Das ist eine bewusste Entscheidung gegen völlige Automatisierung: Ein Echtzeit-Zweiwege-Sync klingt attraktiv, aber er verschleiert genau die vier Bruchstellen, die oben beschrieben sind, statt sie sichtbar zu machen. Wenn jede Änderung sofort und unsichtbar in beide Richtungen propagiert, merkt niemand mehr, wann eine Namenskonvention verletzt wird oder wann ein Composite Token in Figma gar nicht mehr korrekt abgebildet werden kann — der Fehler taucht erst auf, wenn ein Farbwert im Produkt sichtbar falsch ist, und dann ist die Fehlersuche viel teurer, als wenn der Abgleich einmal pro Woche mit einem kurzen Diff-Review passiert.
Wichtig dabei: Dieser wöchentliche Abgleich ist kein Rückschritt gegenüber “voller Automatisierung” — er ist die Erkenntnis, dass volle Automatisierung zwischen zwei strukturell unterschiedlichen Systemen (Figma Variables, CSS Custom Properties) eine Illusion ist, die man sich nicht leisten kann, sobald Composite Tokens, tiefe Aliasing-Ketten oder unterschiedliche Namenskonventionen ins Spiel kommen. Ein kontrollierter, reviewter, aber nicht vollständig automatisierter Sync ist ehrlicher als ein Tool-Versprechen, das die eigentliche Komplexität nur verdeckt, statt sie zu lösen.
Was das für Teams bedeutet, die gerade erst anfangen
Wer heute ein Design-Token-System neu aufsetzt, sollte die Namenskonvention vor der ersten Variable festlegen, nicht danach nachträglich vereinheitlichen — das nachträgliche Aufräumen kostet immer ein Vielfaches der Zeit, die die Vorab-Entscheidung gekostet hätte. Und wer bereits ein bestehendes, driftendes Token-System hat: Der erste Schritt ist nicht, ein Sync-Tool zu kaufen, sondern die vier Bruchstellen oben durchzugehen und für jede zu entscheiden, welche Seite (Figma oder Code) tatsächlich die Source of Truth ist — pro Bruchstelle, nicht pauschal für das gesamte System.
Für Product Leadership übersetzt sich das in eine sehr konkrete Ressourcen-Frage: Ein Team, das glaubt, Figma-Variables-Sync sei ein einmaliges Tooling-Projekt, plant typischerweise einen Sprint dafür ein und wundert sich danach über wiederkehrende Bugtickets zu “Farbe stimmt nicht mit Figma überein”. Ein Team, das versteht, dass es sich um eine dauerhafte, aber gut strukturierbare Übersetzungsaufgabe handelt, plant stattdessen einen wiederkehrenden, aber kleinen wöchentlichen Abgleich ein — 30 Minuten Review statt eines Feuerwehreinsatzes, wenn im Sprint-Review auffällt, dass drei Komponenten seit Wochen die falsche Akzentfarbe zeigen. Das ist der Unterschied zwischen reaktivem Bugfixing, das unvorhersehbar Entwicklerzeit aus laufenden Features abzieht, und einem planbaren, kleinen Wartungsposten, der von Anfang an im Sprint eingepreist ist.
Am Ende ist die Lehre aus allen Projekten, in denen ich Token-Pipelines gebaut habe, dieselbe: Die Synchronisation zwischen Design-Tool und Code ist kein Zustand, den man einmal erreicht — sie ist ein Prozess, den man kontinuierlich pflegt, genau wie man einen Code-Linter oder eine CI-Pipeline pflegt. Wer das akzeptiert und die Bruchstellen kennt, baut ein System, das trotz der strukturellen Unterschiede zwischen Figma und CSS zuverlässig funktioniert. Wer stattdessen auf die eine perfekte Sync-Lösung wartet, die diese Unterschiede unsichtbar macht, wird sie nicht finden — weil das Problem nicht in fehlendem Tooling liegt, sondern in zwei fundamental verschiedenen Datenmodellen, die man übersetzen, nicht gleichsetzen muss.