Eine Legacy-Palette zu Farbskalen migrieren
Das Redesign startete mit einem einzigen Blau. Achtzehn Monate Feature-Arbeit
später lässt eine Designerin, die den nächsten visuellen Durchgang vorbereitet,
ein grep über die Stylesheets laufen und findet 63 verschiedene Farbwerte — 19
davon Blautöne, keine zwei identisch, keiner dokumentiert. Niemand hat das so
entschieden. Jeder Wert war eine vernünftige lokale Entscheidung: ein
Pipetten-Griff aus einem Mockup, ein lighten()-Aufruf, ein Hover-Zustand,
spät an einem Freitag nach Augenmaß angepasst. Die Palette ist nicht
gescheitert; sie ist angewachsen.
Diese Legacy-Palette zu Farbskalen zu refaktorisieren ist eine Migration in fünf Schritten: jeden Farbwert in der Codebasis inventarisieren, die Werte nach ihrer Aufgabe gruppieren statt nach ihrem Aussehen, frische Farbrampen aus der kanonischen Markenfarbe erzeugen, jeden Legacy-Wert auf seinen nächsten Schritt der neuen Rampen abbilden und inkrementell über design tokens migrieren, alt und neu nebeneinander sichtbar. Die meisten Legacy-Werte landen innerhalb einer gerade wahrnehmbaren Differenz zu einem erzeugten Schritt; die wenigen, die das nicht tun, sind Design-Entscheidungen, die getroffen werden müssen — keine Konvertierungen, die man automatisiert. Dieser Artikel geht die fünf Schritte mit einem durchgerechneten Mapping durch; die Rampen-Maschinerie darunter behandeln wir quer durch unseren Leitfaden zu Farbskalen.
Warum sammeln Codebasen so viele Farben an?
Weil jede Farbentscheidung lokal getroffen wird und nichts sie gegen das Ganze abgleicht. Der Entwickler, der einen Rahmen aus einem Mockup nachbaut, kann unmöglich wissen, dass drei Stylesheets weiter bereits ein fast identisches Rahmen-Blau existiert — und zwei Blautöne, die in der Helligkeit ein Hundertstel auseinanderliegen, lassen sich mit bloßem Auge nicht unterscheiden, also kann auch das Code-Review die Dopplung nicht abfangen. Jeder geborgte oder erfundene Wert ist ein kleiner Kredit auf ein künftiges Aufräumen — die Akkumulations-Dynamik, die die Softwaretechnik technische Schuld nennt — und Farbe zahlt ihre Zinsen in Verwirrung: nach genug Krediten kann niemand mehr sagen, welches Blau das echte ist.
Die Anzahl selbst ist ganz normal. Ein Audit der Stylesheets eines Produkts fördert typischerweise irgendwo zwischen 40 und 80 verschiedene Farbwerte zutage, die grob fünfzehn Aufgaben erfüllen — dasselbe Muster, das ein Abstände-Audit findet: viele Werte, die wenigen Absichten dienen. Nicht die Zahl ist die Krankheit, sondern die fehlende Struktur.
Wie inventarisiert man eine Legacy-Palette?
Mechanisch. Durchsuche die Codebasis per grep nach hex-Werten, rgb(), hsl()
und benannten Farben, normalisiere Groß-/Kleinschreibung und Format und zähle
die Vorkommen pro Wert. Beziehe Komponenten-Props und Inline-Styles mit ein,
nicht nur das CSS — Drift versteckt sich in JavaScript. Und halte fest, wo jeder
Wert auftaucht, denn der nächste Schritt braucht Kontext, nicht bloß Koordinaten.
Für die 63-Werte-Codebasis von oben erzählt schon die Verteilung die Geschichte, bevor irgendeine Gruppierung es tut: der häufigste Wert kommt ein paar hundert Mal vor, der Median-Wert drei Mal, und 22 Werte tauchen genau einmal auf. Ein Wert, der nur einmal verwendet wird, ist fast immer Drift — eine einmalige Anpassung, die nie mit irgendetwas abgeglichen wurde.
Sollte man nach Rolle oder nach Ähnlichkeit gruppieren?
Nach Rolle. Ähnlichkeits-Gruppierung — die 63 Werte nach Farbton und Helligkeit zu sortieren — fühlt sich natürlich an und beantwortet die falsche Frage: sie führt bereitwillig die Füllung eines deaktivierten Buttons mit einem Rahmen zusammen, der zufällig dasselbe Grau teilt. Die Frage, die für jeden Wert zählt, ist was will das hier sein? Welche der 19 Blautöne sind Versuche, das Marken-Blau zu treffen? Welche sind Rahmen? Welche sind Einfärbungen für ausgewählte Zeilen? Welche sind Link-Text, den jemand abgedunkelt hat, um den Kontrast zu bestehen?
So sortiert kollabieren die 63 Werte auf grob fünfzehn Rollen — ein Marken-Kern, ein Hover darauf, zwei oder drei Rahmenstärken, eine Familie von Tints, Text auf diesen Tints, eine Handvoll Neutraltöne, die funktionalen Rot- und Grüntöne. Diese Cluster-Anzahl ist die ehrliche Größe der Palette. Alles darüber hinaus ist Rauschen um diese Absichten herum — und genau das macht die Migration handhabbar: du ersetzt nicht 63 Farben, du ersetzt fünfzehn Aufgaben.
Welche Farbe sollte die neuen Rampen speisen?
Die kanonische Markenfarbe — üblicherweise die des Logos, nicht die
meistgenutzte Variante im Code. Der meistgenutzte Wert ist häufig selbst Drift:
eine leicht abweichende Kopie, die sich durch Vervielfältigung durchgesetzt hat
statt durch eine Entscheidung. Für diese Codebasis ist das kanonische Blau
#2563eb, also oklch(0.546 0.215 262.9).
Eine Ausgangsfarbe genügt. Nativ in OKLCH erzeugt, wird sie zu einer zehnstufigen Rampe — die Helligkeit stuft von 0,97 hinunter auf 0,25, das Chroma so geformt, dass es am Marken-Kern seinen Höhepunkt hat und unter der Grenze des Farbraums (Gamut) gedeckelt bleibt, die Schritte mit 50–900 beschriftet — und dieselbe Maschinerie leitet daneben eine getönte Neutralton-Skala und Funktionsfarben ab. Der Sinn des Erzeugens statt erneuten Kuratierens: jeder neue Schritt hat einen genannten Grund — genau das, was den Legacy-Werten fehlte.
Wie bildet man Legacy-Werte auf die neuen Schritte ab?
Konvertiere jeden Legacy-Wert nach OKLCH und finde seinen nächsten Schritt. Hier sind fünf der 19 Blautöne gegen die erzeugte Rampe:
| Legacy-Wert | OKLCH (L / C) | Nächster Schritt | Distanz |
|---|---|---|---|
#4D80E9 | ≈0,62 / 0,17 | 500 #4E82EE (≈0,63 / 0,17) | ΔL ≈0,008 — unter einem JND |
#3B82F6 | ≈0,62 / 0,19 | 500 #4E82EE | ΔL ≈0,002, Farbton ≈3° daneben |
#7FA9F2 | ≈0,73 / 0,12 | 400 #7DA5F2 (≈0,72 / 0,12) | ΔL ≈0,010 — unter einem JND |
#1E56D6 | ≈0,50 / 0,21 | 600 #3A64BA (≈0,52 / 0,14) | ΔL ≈0,017 — aber ΔC ≈0,06, sichtbar kräftiger |
#17A2B8 | ≈0,66 / 0,11, Farbton 212 | — | ≈50° entfernt im Farbton: eine Entscheidung, kein Mapping |
Scale Composer warnt, wenn benachbarte Schritte in der Helligkeit näher als 0,02 aneinanderrücken — eine praxistaugliche gerade wahrnehmbare Differenz — und dieselbe Schwelle liest diese Tabelle. Die ersten drei Zeilen sind Konvertierungen: sie durch ihre Schritte zu ersetzen ist unsichtbar, und der Großteil eines typischen Inventars sieht aus wie sie. Die vierte Zeile ist ein Grenzfall — ihre Helligkeit lässt sich sauber abbilden, aber sie trägt sichtbar mehr Chroma als der Schritt, also muss jemand entscheiden, ob diese zusätzliche Kraft je etwas bedeutet hat. Und das Türkis ist der eigentliche Fund: fünfzig Grad vom Farbton der Palette entfernt, ist es entweder eine undokumentierte Sekundärfarbe oder sechs Jahre alte Drift, und keine Distanz-Metrik kann sagen, welches von beidem. Rechne mit einer Handvoll davon pro Migration; sie sind die eigentliche Design-Arbeit der Migration.
Öffne ein Legacy-zu-Rampe-Mapping in Scale Composer — eine importierte Legacy-Token-Datei mit ihren Werten neben der erzeugten Rampe, die Treffer innerhalb eines JND und die echten Ausreißer auf einen Blick sichtbar.

Kann die Migration inkrementell statt Big-Bang sein?
Ja — und design tokens sind es, die das ermöglichen. Halte während des Übergangs
die Legacy-Namen als Aliase am Leben, die auf ihre gemappten Schritte zeigen:
$blue-old löst sich zu brand/500 auf, beide in derselben Datei sichtbar, und
die Aufrufstellen wechseln in ihrem eigenen Tempo zu
den neuen Namen statt in einem
gefährlichen Rundumschlag.
Der Import von Scale Composer unterstützt das direkt: eine bestehende DTCG-Token-Datei lädt so, wie sie ist, die Farbsektionen lassen sich gegen die erzeugten Rampen anpassen, und alles, was du nicht angefasst hast, übersteht den Round-Trip unverändert — laden, speichern und wieder laden ist ein getesteter Fixpunkt. In der Praxis heißt das: Typografie- und Abstände-Tokens gehen byte-identisch durch, während die Farbe migriert, sodass der erste Pull Request eine einzige Rampe sein kann, nicht das ganze System.
Werden Nutzer die neue Palette bemerken?
Ehrlich: vielleicht — und etwas anderes vorzugeben ist der Grund, warum Migrationen zurückgerollt werden. Jede einzelne Verschiebung in der Tabelle oben ist unsichtbar; genau das bedeutet ein Mapping innerhalb eines JND. Aber vierzig Fast-JND-Verschiebungen auf einem Screen sind nicht vierzig unabhängige Ereignisse. Wenn die meisten in dieselbe Richtung tendieren — etwas kühler, etwas heller — ändert sich die Gesamttemperatur der Seite, obwohl kein einzelnes Element es sichtbar getan hat. Vergleiche die Schlüssel-Screens per Screenshot-Diff vorher und nachher und bewerte die Unterschiede als Design-Entscheidung, statt sie als Rauschen abzutun. Meist ist das neue Rendering auch das bessere — die Verschiebungen zeigen zur Kurve hin statt von ihr weg — aber das ist eine Entscheidung, die man mit offenen Augen trifft.
Der risikoärmste Einstieg ist zu sehen, wie klein der erste Schritt sein kann. Lade eine Legacy-Token-Datei und exportiere den Diff — die Marken-Rampe umgeschrieben auf erzeugte Schritte, jede Sektion, die du nicht angefasst hast, byte-für-byte intakt — und die Migration wird zu einer Reihe überprüfbarer Inkremente statt zu einem Sprung.