Mis à jour 10 juillet 2026

Migrer une palette héritée vers des échelles de couleur

La refonte est partie avec un seul bleu. Dix-huit mois de développement de fonctionnalités plus tard, un designer qui prépare la prochaine passe visuelle lance un grep sur les feuilles de style et trouve 63 valeurs de couleur distinctes — dont 19 bleus, aucun identique à un autre, aucun documenté. Personne n’a décidé cela. Chaque valeur était un choix local raisonnable : une pipette prélevée sur une maquette, un appel à lighten(), un état de survol ajusté à l’œil un vendredi soir. La palette n’a pas échoué ; elle s’est accumulée.

Refactoriser cette palette héritée en échelles de couleur est une migration en cinq étapes : inventorier chaque valeur de couleur dans le code, regrouper les valeurs selon le rôle qu’elles jouent plutôt que selon leur apparence, générer de nouvelles rampes à partir de la couleur de marque canonique, mapper chaque valeur héritée sur le pas le plus proche des nouvelles rampes, et migrer progressivement via les tokens en gardant l’ancien et le nouveau côte à côte. La plupart des valeurs héritées tombent en deçà d’une différence à peine perceptible d’un pas généré ; la poignée qui n’y parvient pas relève de décisions de design à prendre, pas de conversions à automatiser. Cet article parcourt les cinq étapes avec un exemple de mapping ; la mécanique des rampes en dessous est couverte dans notre guide des échelles de couleur.

Pourquoi les bases de code accumulent-elles autant de couleurs ?

Parce que chaque décision de couleur est prise localement, et rien ne la confronte à l’ensemble. Le développeur qui ajuste une bordure d’après une maquette n’a aucun moyen de savoir qu’un bleu de bordure presque identique existe déjà trois feuilles de style plus loin — et deux bleus séparés d’un centième de luminosité sont indiscernables à l’œil, donc la revue de code ne peut pas non plus repérer la duplication. Chaque valeur empruntée ou inventée est un petit emprunt sur un nettoyage futur — cette dynamique d’accumulation que le génie logiciel appelle la dette technique — et la couleur paie ses intérêts en confusion : après assez d’emprunts, personne ne peut dire quel bleu est le vrai.

Le décompte en lui-même est ordinaire. Auditer les feuilles de style d’un produit fait généralement apparaître entre 40 et 80 valeurs de couleur distinctes remplissant une quinzaine de rôles — la même forme qu’un audit d’espacement révèle, beaucoup de valeurs au service de peu d’intentions. Le nombre n’est pas la maladie ; c’est la structure manquante qui l’est.

Comment inventorier une palette héritée ?

Mécaniquement. Faites un grep du code pour les valeurs hex, rgb(), hsl() et les couleurs nommées, normalisez la casse et le format, et comptez les occurrences par valeur. Incluez les props de composants et les styles inline, pas seulement le CSS — la dérive se cache dans le JavaScript. Et notez où chaque valeur apparaît, car l’étape suivante a besoin de contexte, pas seulement de coordonnées.

Pour la base de code à 63 valeurs ci-dessus, la distribution raconte l’histoire avant tout regroupement : la valeur la plus fréquente apparaît quelques centaines de fois, la valeur médiane trois fois, et 22 valeurs apparaissent exactement une seule fois. Une valeur utilisée une seule fois est presque toujours de la dérive — un ajustement ponctuel jamais réconcilié avec quoi que ce soit.

Faut-il regrouper par rôle ou par similarité ?

Par rôle. Le regroupement par similarité — trier les 63 valeurs par teinte et luminosité — paraît naturel et répond à la mauvaise question : il fusionnera allègrement le remplissage d’un bouton désactivé avec une bordure qui partage par hasard son gris. La question qui compte pour chaque valeur est qu’est-ce que ça cherche à être ? Lesquels des 19 bleus sont des tentatives de reproduire le bleu de marque ? Lesquels sont des bordures ? Lesquels sont des voiles de ligne sélectionnée ? Lesquels sont du texte de lien que quelqu’un a assombri pour passer le contraste ?

Triées ainsi, les 63 valeurs se réduisent à une quinzaine de rôles — un cœur de marque, un survol dessus, deux ou trois épaisseurs de bordure, une famille de nuances claires, du texte sur ces nuances, une poignée de neutres, les rouges et verts fonctionnels. Ce nombre de groupes est la taille honnête de la palette. Tout ce qui va au-delà est du bruit autour de ces intentions, et c’est ce qui rend la migration abordable : vous ne remplacez pas 63 couleurs, vous remplacez quinze rôles.

Quelle couleur doit servir de départ aux nouvelles rampes ?

La couleur de marque canonique — généralement celle du logo, pas la variante la plus utilisée dans le code. La valeur la plus utilisée est souvent elle-même de la dérive : une copie légèrement décalée qui a gagné par réplication plutôt que par décision. Pour cette base de code, le bleu canonique est #2563eb, soit oklch(0.546 0.215 262.9).

Une seule couleur de départ suffit. Générée nativement en OKLCH, elle devient une rampe de dix pas — la luminosité descendant par paliers de 0,97 à 0,25, le chroma modelé pour culminer au cœur de marque et bridé sous le plafond du gamut, les pas étiquetés 50–900 — et la même mécanique dérive au passage une échelle de neutres teintés et des couleurs fonctionnelles. Tout l’intérêt de générer plutôt que de re-curer : chaque nouveau pas a une raison énoncée, précisément ce qui manquait aux valeurs héritées.

Comment mapper les valeurs héritées sur les nouveaux pas ?

Convertissez chaque valeur héritée en OKLCH et trouvez son pas le plus proche. Voici cinq des 19 bleus confrontés à la rampe générée :

Valeur héritéeOKLCH (L / C)Pas le plus procheDistance
#4D80E9≈0.62 / 0.17500 #4E82EE (≈0.63 / 0.17)ΔL ≈0.008 — en deçà d’un JND
#3B82F6≈0.62 / 0.19500 #4E82EEΔL ≈0.002, teinte décalée d’≈3°
#7FA9F2≈0.73 / 0.12400 #7DA5F2 (≈0.72 / 0.12)ΔL ≈0.010 — en deçà d’un JND
#1E56D6≈0.50 / 0.21600 #3A64BA (≈0.52 / 0.14)ΔL ≈0.017 — mais ΔC ≈0.06, visiblement plus vif
#17A2B8≈0.66 / 0.11, teinte 212à ≈50° en teinte : une décision, pas un mapping

Scale Composer alerte quand deux pas voisins se rapprochent à moins de 0,02 l’un de l’autre en luminosité — une différence à peine perceptible opérationnelle — et le même seuil se lit dans ce tableau. Les trois premières lignes sont des conversions : les échanger contre leur pas est invisible, et la majeure partie d’un inventaire typique leur ressemble. La quatrième ligne est limite — sa luminosité se mappe proprement mais elle porte visiblement plus de chroma que le pas, donc quelqu’un doit décider si ce surplus de vivacité a jamais voulu dire quelque chose. Et le turquoise est la vraie trouvaille : à cinquante degrés de la teinte de la palette, c’est soit une couleur secondaire non documentée, soit une dérive vieille de six ans, et aucune métrique de distance ne peut trancher. Attendez-vous à une poignée de ces cas par migration ; c’est là le vrai travail de design de la migration.

Ouvrir un mapping palette héritée-vers-rampe dans Scale Composer — un fichier de tokens hérité importé, ses valeurs aux côtés de la rampe générée, les correspondances à moins d’un JND et les véritables aberrations visibles d’un coup d’œil.

Les valeurs bleues d'une palette héritée mappées contre une rampe de dix pas fraîchement générée dans Scale Composer, la plupart tombant en deçà d'une différence à peine perceptible d'un pas

La migration peut-elle être progressive plutôt que d’un seul bloc ?

Oui — et ce sont les tokens qui le permettent. Pendant la transition, gardez les noms hérités en vie comme des alias pointant vers leurs pas mappés : $blue-old se résout en brand/500, les deux visibles dans le même fichier, et les points d’appel migrent vers les nouveaux noms à leur propre rythme plutôt qu’en un seul balayage périlleux.

L’import de Scale Composer prend cela en charge directement : un fichier de tokens DTCG existant se charge tel quel, les sections de couleur peuvent être ajustées par rapport aux rampes générées, et tout ce que vous n’avez pas touché survit à l’aller-retour sans changement — charger, enregistrer et recharger est un point fixe testé. En pratique, cela veut dire que les tokens de typographie et d’espacement traversent octet pour octet identiques pendant que la couleur migre, si bien que la première pull request peut porter sur une seule rampe, pas sur tout le système.

Les utilisateurs remarqueront-ils la nouvelle palette ?

Honnêtement : peut-être — et prétendre le contraire, c’est ainsi qu’on finit par annuler les migrations. Chaque décalage individuel du tableau ci-dessus est invisible ; c’est ce que signifie mapper en deçà d’un JND. Mais quarante décalages proches du JND sur un même écran ne sont pas quarante événements indépendants. Si la plupart penchent dans le même sens — un peu plus froids, un peu plus clairs — la température globale de la page change alors même qu’aucun élément ne l’a fait de façon visible. Faites un diff par capture d’écran des écrans clés avant et après, et examinez les différences comme une décision de design plutôt que de les écarter comme du bruit. En général, le nouveau rendu est aussi le meilleur — les décalages pointent vers la courbe plutôt que de s’en éloigner — mais c’est un choix à faire en toute lucidité.

La façon la moins risquée de commencer, c’est de voir à quel point la première étape peut être modeste. Chargez un fichier de tokens hérité et exportez le diff — la rampe de marque réécrite en pas générés, chaque section que vous n’avez pas touchée intacte octet pour octet — et la migration devient une série d’incréments révisables plutôt qu’un saut.

Continuer la lecture