Migrar una paleta heredada a escalas de color
El rediseño se lanzó con un solo azul. Dieciocho meses de trabajo de
funcionalidades después, una diseñadora que prepara la siguiente pasada visual
ejecuta un grep sobre las hojas de estilo y encuentra 63 valores de color
distintos: 19 de ellos azules, ninguno idéntico a otro, ninguno documentado.
Nadie decidió esto. Cada valor fue una elección local razonable: una toma con
el cuentagotas de un mockup, una llamada a lighten(), un estado hover
ajustado a ojo un viernes por la tarde. La paleta no falló; se fue acumulando.
Refactorizar esa paleta heredada en escalas de color es una migración de cinco pasos: inventaría cada valor de color del código, agrupa los valores por el trabajo que hacen más que por cómo se ven, genera rampas nuevas sembradas a partir del color de marca canónico, mapea cada valor heredado a su paso más cercano en las rampas nuevas y migra de forma incremental mediante design tokens con lo viejo y lo nuevo visibles lado a lado. La mayoría de los valores heredados caen dentro de una diferencia apenas perceptible de un paso generado; los pocos que no lo hacen son decisiones de diseño que tomar, no conversiones que automatizar. Este artículo recorre los cinco pasos con un mapeo trabajado; la maquinaria de rampas que hay debajo se cubre a lo largo de nuestra guía de escalas de color.
¿Por qué las bases de código acumulan tantos colores?
Porque cada decisión de color se toma en local, y nada la compara con el conjunto. El desarrollador que iguala un borde de un mockup no tiene forma de saber que ya existe un azul de borde casi idéntico tres hojas de estilo más allá, y dos azules separados por una centésima en luminosidad no se distinguen a simple vista, así que la revisión de código tampoco puede detectar la duplicación. Cada valor prestado o inventado es un pequeño préstamo contra una futura limpieza —la dinámica de acumulación que la ingeniería de software llama deuda técnica— y el color paga sus intereses en confusión: tras suficientes préstamos, nadie puede decir cuál azul es el de verdad.
El recuento en sí es corriente. Auditar las hojas de estilo de un producto suele arrojar entre 40 y 80 valores de color distintos haciendo unos quince trabajos: la misma forma que encuentra una auditoría de espaciado, muchos valores sirviendo a pocas intenciones. El número no es la enfermedad; lo es la estructura que falta.
¿Cómo se inventaría una paleta heredada?
Mecánicamente. Haz grep en el código en busca de valores hex, rgb(), hsl()
y colores con nombre, normaliza mayúsculas y formato, y cuenta las apariciones
por valor. Incluye las props de componentes y los estilos en línea, no solo el
CSS: la deriva se esconde en JavaScript. Y anota dónde aparece cada valor,
porque el siguiente paso necesita contexto, no solo coordenadas.
Para el código de 63 valores de arriba, la distribución cuenta la historia antes que cualquier agrupamiento: el valor más frecuente aparece unos cientos de veces, el valor mediano tres veces, y 22 valores aparecen exactamente una vez. Un valor usado una sola vez es casi siempre deriva: una coincidencia puntual que nunca se reconcilió con nada.
¿Deberías agrupar por rol o por similitud?
Por rol. El agrupamiento por similitud —ordenar los 63 valores por tono y luminosidad— parece natural y responde a la pregunta equivocada: fusionará sin problema el relleno de un botón deshabilitado con un borde que resulta compartir su gris. La pregunta que importa para cada valor es ¿qué intenta ser esto? ¿Cuáles de los 19 azules son intentos del azul de marca? ¿Cuáles son bordes? ¿Cuáles son veladuras de fila seleccionada? ¿Cuáles son texto de enlace que alguien oscureció para pasar el contraste?
Ordenados así, los 63 valores se colapsan en unos quince roles: un núcleo de marca, un hover sobre él, dos o tres grosores de borde, una familia de tintes, texto sobre esos tintes, un puñado de neutros, los rojos y verdes funcionales. Ese recuento de grupos es el tamaño honesto de la paleta. Todo lo que sobra es ruido alrededor de esas intenciones, y eso es lo que hace la migración manejable: no estás reemplazando 63 colores, estás reemplazando quince trabajos.
¿Qué color debería sembrar las rampas nuevas?
El color de marca canónico: normalmente el del logo, no la variante más usada
en el código. El valor más usado suele ser deriva en sí mismo: una copia
ligeramente desviada que ganó por replicación y no por decisión. Para este
código el azul canónico es #2563eb, que es oklch(0.546 0.215 262.9).
Basta con una semilla. Generada de forma nativa en OKLCH, se convierte en una rampa de diez pasos: la luminosidad baja por pasos desde 0,97 hasta 0,25, el croma modelado para alcanzar su pico en el núcleo de marca y limitado bajo el techo de gama, con los pasos etiquetados de 50 a 900, y la misma maquinaria deriva junto a ella una escala de neutros teñidos y los colores funcionales. La razón de generar en lugar de recurar: cada paso nuevo tiene un motivo declarado, que es precisamente lo que les faltaba a los valores heredados.
¿Cómo se mapean los valores heredados a los pasos nuevos?
Convierte cada valor heredado a OKLCH y encuentra su paso más cercano. Aquí van cinco de los 19 azules contra la rampa generada:
| Valor heredado | OKLCH (L / C) | Paso más cercano | Distancia |
|---|---|---|---|
#4D80E9 | ≈0,62 / 0,17 | 500 #4E82EE (≈0,63 / 0,17) | ΔL ≈0,008 — por debajo de un JND |
#3B82F6 | ≈0,62 / 0,19 | 500 #4E82EE | ΔL ≈0,002, tono desviado ≈3° |
#7FA9F2 | ≈0,73 / 0,12 | 400 #7DA5F2 (≈0,72 / 0,12) | ΔL ≈0,010 — por debajo de un JND |
#1E56D6 | ≈0,50 / 0,21 | 600 #3A64BA (≈0,52 / 0,14) | ΔL ≈0,017 — pero ΔC ≈0,06, visiblemente más vívido |
#17A2B8 | ≈0,66 / 0,11, tono 212 | — | a ≈50° de distancia en tono: una decisión, no un mapeo |
Scale Composer avisa cuando pasos vecinos quedan a menos de 0,02 entre sí en luminosidad —una diferencia apenas perceptible en la práctica— y el mismo umbral lee esta tabla. Las tres primeras filas son conversiones: cambiarlas por sus pasos es invisible, y la mayoría de un inventario típico se parece a ellas. La cuarta fila está en el límite: su luminosidad mapea de forma limpia pero arrastra visiblemente más croma que el paso, así que alguien tiene que decidir si esa vividez de más significó algo alguna vez. Y el turquesa es el hallazgo de verdad: a cincuenta grados del tono de la paleta, es o bien un color secundario sin documentar o bien deriva de seis años, y ninguna métrica de distancia puede decir cuál de los dos. Espera un puñado de estos por migración; son el verdadero trabajo de diseño de la migración.
Abre un mapeo de heredado a rampa en Scale Composer — un archivo de tokens heredado importado con sus valores junto a la rampa generada, con las coincidencias dentro de un JND y los verdaderos valores atípicos visibles de un vistazo.

¿Puede la migración ser incremental en lugar de de golpe?
Sí, y los design tokens son lo que lo hace posible. Durante la transición,
mantén vivos los nombres heredados como alias que apuntan a sus pasos mapeados:
$blue-old resuelve a brand/500, ambos visibles en el mismo archivo, y los
puntos de uso migran a los nombres nuevos
a su propio ritmo en lugar de en un barrido peligroso.
La importación de Scale Composer soporta esto directamente: un archivo de tokens DTCG existente se carga tal cual, las secciones de color pueden ajustarse contra las rampas generadas, y todo lo que no tocaste sobrevive el viaje de ida y vuelta sin cambios: cargar, guardar y volver a cargar es un punto fijo probado. En la práctica eso significa que los tokens de tipografía y espaciado pasan idénticos byte a byte mientras el color migra, así que la primera pull request puede ser una sola rampa, no todo el sistema.
¿Notarán los usuarios la paleta nueva?
Con sinceridad: puede que sí, y fingir lo contrario es como las migraciones acaban revertidas. Cada cambio individual de la tabla de arriba es invisible; eso es lo que significa mapear dentro de un JND. Pero cuarenta cambios cercanos a un JND en una misma pantalla no son cuarenta eventos independientes. Si la mayoría se inclinan en la misma dirección —un poco más fríos, un poco más claros— la temperatura general de la página cambia aunque ningún elemento aislado lo haga de forma visible. Compara mediante captura de pantalla las pantallas clave antes y después, y revisa las diferencias como una decisión de diseño en lugar de descartarlas como ruido. Normalmente el nuevo renderizado también es el mejor —los cambios apuntan hacia la curva en lugar de alejarse de ella— pero es una decisión que hay que tomar con los ojos abiertos.
La forma menos arriesgada de empezar es ver lo pequeño que puede ser el primer paso. Carga un archivo de tokens heredado y exporta el diff — la rampa de marca reescrita a pasos generados, cada sección que no tocaste intacta byte a byte — y la migración se convierte en una serie de incrementos revisables en lugar de un salto.