Construcción de una escala cromática accesible

por Pau | Caso de estudio

Experimento para construir una escala cromática usable en interfaces de usuario y probar su comportamiento en contraste bajo estándares WCAG, estados de interfaz y light/dark mode.

Resumen

Los sistemas de diseño más populares, como Material Design y Angular Material, suelen usar escalas de color (50-900) para generar tokens reutilizables e implementar estilos de forma escalable.

Como profesional autodidacta, este ejercicio nace de una pregunta: ¿cómo construir una paleta de color funcional para una interfaz, sin depender de la intuición?

El desafío era diseñar una escala de color consistente usando HSL que cumpla con estándares de accesibilidad mínimos (AA).

La escala de color debía cumplir con las siguientes condiciones:

  • Monocromática
  • Escala 50-900
  • Pruebas de contraste para accesibilidad
  • Pruebas en interfaz
  • Compatibilidad con light/dark mode

Resultado:

  • Escala HSL 50-900 consistente
  • Contraste evaluado contra WCAG
  • Tokens implementados como variables de CSS
  • Test en componentes UI (botones, textos fondos)
  • Experimento de dark mode con data-attributes

Hallazgos:

  • En HSL, variaciones lineales en saturación y luminosidad no producen cambios perceptuales uniformes, por lo que la construcción de una escala consistente requiere ajustes manuales.
  • Una única escala cromática no garantiza contraste accesible universal (WCAG AAA) en todos sus niveles y contextos de uso.
  • Adaptar una escala a dark mode no es solo una inversión simétrica de valores; es necesario recalibrar y compensar para mantener jerarquía visual y legibilidad.
  • Las escalas de color en sistemas de diseño no necesitan ser perceptualmente perfectas; deben ser funcionales en contextos de interfaz, priorizando contraste funcional y jerarquía visual.
  • Implementar la escala mediante arquitectura de tokens (variables CSS) y data-attributes permite construir una base de theming escalable para un sistema de diseño.
  • La pregunta que nos deberíamos hacer al diseñar una paleta no debería ser "¿qué colores pongo?" sino "¿qué relaciones de color necesita mi interfaz?"

Las escalas HSL 50-900 suelen ser usadas en sistemas de diseño, como Material Design, para representar la variación en brillo de un color, que va desde los colores más claros (siendo el 50 el más cercano al blanco), hasta los más oscuros (siendo el 900 el más cercano al negro). Esto permite generar armonías monocromáticas consistentes.

Purple 50 H: 292° S: 44% L: 93%
Purple 100 H: 291° S: 46% L: 83%
Purple 200 H: 291° S: 47% L: 71%
Purple 300 H: 291° S: 47% L: 60%
Purple 400 H: 291° S: 47% L: 51%
Purple 500 H: 291° S: 64% L: 42%
Purple 600 H: 287° S: 65% L: 40%
Purple 700 H: 282° S: 68% L: 38%
Purple 800 H: 277° S: 70% L: 35%
Purple 900 H: 267° S: 75% L: 31%
Ejemplo de paleta cromática en escala 50-900 de Material Design. Se ve una ligera variación de matiz (H) y saturación (S), que incrementan hacia los colores más oscuros. Al revés sucede con la luminosidad (L), que va decrementando sostenidamente hacia los colores oscuros.

¿Por qué HSL?

Para este experimento decidí usar el modelo HSL, ya que a diferencia de los modelos hexadecimal y RGB, permite manipular el color de forma más intuitiva a través del matiz, la saturación y la luminosidad.

Entender cómo afectan estos parámetros al color es fundamental para crear escalas consistentes, ya que en valores extremos de la escala, la identidad del color base puede perderse.

Para contextualizar, primero es útil responder ¿qué es HSL?

A grandes rasgos es un modelo de color parametrizado por tres propiedades clave:

  1. Matiz (H, Hue): representa la identidad cromática del color. Se mide en grados desde el 0° al 360° siendo en ambos extremos rojo.
H: 0° — hsl(0, 100%, 50%)
Al arrastrar el puntero alrededor de la rueda se observa el cambio en el matiz (Hue) expresado en grados.
  1. Saturación (S, Saturation): representa la intensidad o "pureza" del color. Indica cuánta presencia de gris hay en él. A menor saturación, el matiz se debilita progresivamente al punto en que, con 0%, desaparece completamente y el resultado es un tono de gris determinado únicamente por la luminosidad. A mayor saturación, el color se muestra con mayor intensidad, siendo el 100% su máxima expresión dentro de los límites que la pantalla puede mostrar para ese matiz.
S: 0% — hsl(0, 50%, 50%)
Al arrastrar el puntero sobre la barra se observa el cambio en la saturación del color (S) expresado en porcentaje (0% - 100%).
  1. Luminosidad (L, Lightness): mide cuánta luz tiene el color en una escala entre blanco y negro. A mayor luminosidad, el color es más claro y se aproxima al blanco (100%), mientras que a menor luminosidad, el color se oscurece progresivamente hasta llegar al negro (0%). En torno al 50% de luminosidad, el color se muestra en su punto medio, sin predominancia del blanco o del negro en su mezcla.
L: 0% — hsl(240, 100%, 50%)
Al arrastrar el puntero sobre la barra se observa el cambio en la luminosidad del color (L) expresado en porcentaje (0% - 100%).

Definiendo el color base: hipótesis del rango seguro

Ahora que entiendo qué es el sistema de color HSL, voy a construir mi paleta sobre un color base que será el valor 500 de mi escala.

El matiz puede ser cualquiera, ya que representa la identidad del color en sí. Pero, ¿qué pasa con la saturación y la luminosidad?

Variación de Luminosidad

Variación de Saturación

Herramienta interactiva para comparar variaciones de luminosidad y saturación basadas en un color HSL seleccionado. A primera vista se aprecia el cambio en el color cuando hay una variación de 20% en luminosidad y saturación, hacia arriba y hacia abajo, respecto del color base representado al centro.

Al mover los controles de saturación y luminosidad queda en evidencia la pérdida de identidad del color en saturaciones muy bajas. Al contrario, saturaciones muy altas producen colores demasiado vibrantes. Algo similar ocurre con la luminosidad, valores altos tienden rápidamente al blanco, mientras que valores bajos se acercan al negro.

Entonces ¿cómo lograr un equilibrio entre los valores S y L de forma tal que pueda aumentar y disminuir con espacio suficiente para crear los valores restantes?

Hipótesis

Propongo la siguiente hipótesis: los colores base más estables para construir escalas HSL utilizables en interfaz tienden a ubicarse en un rango medio de saturación y luminosidad (aprox. 45–60%).

Esta hipótesis no pretende ser una regla universal, sino que un punto de partida razonable para construir escalas de interfaz.

El rango sugerido deja espacio tanto para aumentar como disminuir la luminosidad, y margen para ajustar la saturación sin perder identidad del color.

Experimento

Para no guiarme solo por la intuición matemática de "estoy justo al medio", voy a observar el comportamiento de dos matices diferentes respecto de su luminosidad. Para aislar el efecto de L sobre el color, mantendré constantes el matiz y la saturación.

Voy a generar siete colores diferentes para cada matiz: dos con un porcentaje inferior al rango propuesto, dos con un porcentaje superior al rango propuesto, los límites del rango (45% y 60%), y 50%.

L: 25%
L: 35%
L: 45%
L: 50%
L: 60%
L: 70%
L: 80%
Comparación de colores obtenidos al variar solo el parámetro de luminosidad en un color rojo con 60% de saturación.
L: 25%
L: 35%
L: 45%
L: 50%
L: 60%
L: 70%
L: 80%
Comparación de colores obtenidos al variar solo el parámetro de luminosidad en un color azul con 60% de saturación.

Los cuatro valores extremos de cada fila, al tener valores L muy altos o muy bajos, dificultan (o simplemente no permiten) escoger los valores extremos de mi escala 50-900, si los escogiera como color base.

Para ejemplificar, voy a tomar el color con L: 35% de la fila de los rojos y de los azules, para crear una escala 50-900 modificando solo el parámetro de luminosidad de forma lineal, en incrementos y decrementos de 5%.

50 L: 60%
100 L: 55%
200 L: 50%
300 L: 45%
400 L: 40%
500 L: 35%
600 L: 30%
700 L: 25%
800 L: 20%
900 L: 15%
Escala 50-900, creada a partir del color HSL(0°, 60%, 35%) como color base (500).
50 L: 60%
100 L: 55%
200 L: 50%
300 L: 45%
400 L: 40%
500 L: 35%
600 L: 30%
700 L: 25%
800 L: 20%
900 L: 15%
Escala 50-900, creada a partir del color HSL(240°, 60%, 35%) como color base (500).

En la escala roja, existen saltos abruptos en el color entre un nivel y otro, mientras que los valores bajo 35% de luminosidad se sienten densos y no muy distintos entre ellos.

Por otra parte, la escala azul tiene saltos que parecen, en general, más uniformes. Pero la paleta también se siente poco variada, y con tendencia a apretarse hacia menor luminosidad.

La densidad hacia los colores más oscuros de la escala ocurre, precisamente, porque partí de un color con luminosidad baja; resultando en menos espacio para generar colores variados desde el nivel 500.

Al respecto, modificar la saturación me daría más opciones, además de mejorar los tonos sucios y deslavados de los extremos. Pero por ahora quiero enfocarme solo en luminosidad.

Si tomo en cuenta que usualmente en una escala 50-900 los niveles:

  • 50 a 200 se usan como background-color (en light themes),
  • 300 a 600 para componentes de interfaz base (como botones, cards, etc),
  • y los valores más oscuros para textos y énfasis fuertes (texto sobre fondo claro, íconos importantes, alertas, encabezados, enlace activo),

estas paletas no parecieran tener el contraste suficiente para, por ejemplo, diseñar un botón con background-color 50 y texto 700.

Botones diseñados con "background-color" de nivel 50, y color de texto nivel 700 para las paletas roja y azul.
Resultado del experimento

Queda a la vista que la legibilidad del texto es pobre, ya que no genera suficiente contraste.

Cada botón redirige a la prueba de contraste en el sitio WebAIM.org, pero de todas formas dejo la evaluación de contraste a continuación.

HSL(0°, 60%, 60%)
HSL(0°, 60%, 25%)
Ratio de contraste 3.22:1

Texto normal

  • WCAG AA: Falla
  • WCAG AAA: Falla

Texto grande

  • WCAG AA: Pasa
  • WCAG AAA: Falla

Componentes UI y objetos gráficos

  • WCAG AA: Pasa
HSL(0°, 60%, 60%)
HSL(0°, 60%, 25%)
Ratio de contraste 2.88:1

Texto normal

  • WCAG AA: Falla
  • WCAG AAA: Falla

Texto grande

  • WCAG AA: Falla
  • WCAG AAA: Falla

Componentes UI y objetos gráficos

  • WCAG AA: Falla

Los ratios de contraste fallan claramente en ambos casos. Esto es porque el color más claro de las paletas solo alcanza un 60% de luminosidad, apenas sobre la mitad de su luminosidad posible. De hecho, la paleta roja tiene un puntaje de contraste mejor que la azul. ¿Por qué? Porque el ojo humano no percibe el color de forma lineal. El azul es perceptualmente más oscuro que el resto, y el verde el que se percibe más claro. Vale la pena considerar esto al momento de escoger una luminosidad y saturación base.

Este comportamiento sugiere que el problema no es solo el color elegido, sino desde dónde se empieza en el eje de luminosidad. Cuando el valor es demasiado bajo, gran parte del rango disponible queda concentrado hacia los niveles más oscuros de la escala.

Debido a la naturaleza logarítmica de la percepción del brillo (Ley de Weber-Fechner), el ojo humano distingue peor entre colores muy oscuros que entre colores más claros. Esto hace que las diferencias entre niveles se perciban comprimidas y la escala pierde variedad.

¿Podría variar de manera no lineal, aumentando la variación de luminosidad hacia el valor 50 de la escala, mientras que los niveles sobre el 500 se mantienen con diferencia comprimida, y quizá llegando a luminosidad 10% para el nivel 900?

Sí, eso me daría mucho espacio para definir los colores claros, pero seguiría con un rango reducido en los colores oscuros. Probablemente tendría flexibilidad en light mode, pero si quisiera usar la misma paleta para dark mode carecería del contraste necesario para que los colores funcionen en la interfaz porque los valores sobre el 700 no se diferenciarían mucho entre sí.

En conclusión, creo que partir con un color HSL de valor L menor a 40% no es una mala idea en sí, pero debe escogerse estratégicamente. Si quisiera diseñar una paleta intensa, con carácter fuerte, o un sistema predominantemente oscuro, podría funcionar sacrificando flexibilidad, espacio respirable y fondos claros. Esto refuerza una idea importante: no existe una fórmula única para escoger un color base universalmente accesible. La accesibilidad siempre va a depender del contexto de uso dentro de la interfaz.

Construcción de la escala final

Definición del color base

El matiz escogido es 160° (verde). Dada mi investigación anterior, considero que la luminosidad es el parámetro más crítico, y como el verde es el color perceptualmente más brillante, tiene más resistencia a perder identidad en los valores claros de la escala, lo que lo hace más predecible para este experimento.

Por este mismo motivo, voy a fijar la luminosidad de mi color base en 50%, justo en el centro del rango.

La saturación será de 60% porque en 50% el color base se percibía muy opaco.

Con esto, ambos valores (S y L) cumplen con estar dentro del rango de seguridad propuesto (entre 45% y 60%).

Color verde base para la construcción de escala 50-900. Este color corresponderá al nivel 500 de dicha escala.
Escala 50 - 900 completa

Para dejar la escala lo más equilibrada posible, ajusté luminosidad y saturación en cada paso, pero mantuve el hue intacto.

En los valores más claros de la escala, me di cuenta que a mayor luminosidad, la identidad del color se va perdiendo, por lo que compensé con mayor saturación del color.

Nivel Antes Después
50 HSL(160°, 60%, 90%) HSL(160°, 80%, 90%)
100 HSL(160°, 60%, 85%) HSL(160°, 85%, 85%)
200 HSL(160°, 60%, 80%) HSL(160°, 80%, 80%)
Diferencia entre los colores escogidos en principio y los definitivos luego de ajustes en la saturación para evitar pérdida de identidad en los colores claros.

Los valores sobre el 600 significaron una dificultad adicional, ya que en mi primer intento dejé la saturación de los niveles 700, 800 y 900 en 70%, 80% y 85% respectivamente, produciendo colores muy activos y vibrantes que competirían con la zona media de mi paleta.

Nivel Original Ajustado
700 HSL(160°, 70%, 32%) HSL(160°, 55%, 32%)
800 HSL(160°, 80%, 25%) HSL(160°, 50%, 25%)
900 HSL(160°, 85%, 18%) HSL(160°, 40%, 18%)
Diferencia entre los colores escogidos en principio y los definitivos luego de ajustes en la saturación para mantener la cohesión visual en niveles oscuros.

Finalizando estos ajustes, esta es la escala producida:

50 HSL(160°, 80%, 90%)
100 HSL(160°, 85%, 85%)
200 HSL(160°, 80%, 80%)
300 HSL(160°, 70%, 75%)
400 HSL(160°, 60%, 65%)
500 HSL(160°, 60%, 50%)
600 HSL(160°, 60%, 40%)
700 HSL(160°, 55%, 32%)
800 HSL(160°, 50%, 25%)
900 HSL(160°, 40%, 18%)
Escala 50-900, creada a partir del color HSL(160°, 60%, 50%) como color base (500).

Con la escala finalmente definida, el siguiente paso es someterla a pruebas de usabilidad práctica y accesibilidad.

Pruebas de validación

Prueba 1: Contraste

Medir ratios de contraste con estándar AA es el mínimo indispensable para validar usabilidad. Para determinar si mi escala cumple con los valores mínimos de accesibilidad WCAG, voy a hacer dos tipos de validaciones:

  1. Validación técnica de referencia. Voy a medir el contraste entre varios niveles de la escala producida versus blanco y negro.
  2. Validación práctica. Voy a medir el contraste entre colores de la misma escala.
Pero primero, ¿qué es WCAG?

WCAG son las siglas en inglés de "Pautas de accesibilidad al contenido en la web", y estas reglas las pone el World Wide Web Consortium (más conocido como W3C).

Estas pautas definen cómo hacer que el contenido web sea más accesible para personas con diversas discapacidades, ya sean visuales, auditivas, motrices, cognitivas o neurológicas; además de mejorar la experiencia para adultos mayores con capacidades reducidas por el envejecimiento. Aunque su objetivo principal es la accesibilidad, en la práctica aplicar estas pautas suele traducirse en una mejor usabilidad para todos los usuarios.

Esta prueba se enfocará en la accesibilidad visual, específicamente el contraste entre colores de la escala 50-900 producida, blanco y negro.

Para asegurar el contraste debemos calcular la luminancia relativa en estándar RGB. La luminancia relativa mide el brillo percibido de un color en comparación con un blanco de referencia, normalizado en una escala de 0 (negro) a 1 (blanco).

Para esto es necesario tener los valores RGB del color a evaluar, para luego convertirlo a sRGB lineal. Lo que hace esta conversión es eliminar la corrección gamma.

Nuestros ojos perciben los colores de forma no-lineal, así que cuando se definió el estándar sRGB se "guardaron" los colores de forma no-lineal para que se ajuste mejor a cómo ve el ojo humano. Esta corrección es la que se necesita revertir para calcular la luminancia relativa.

  1. Normalización de los canales

Se toma el calor de los canales R, G y B (C8bitC_{8bit}) y se divide por 255 para llevarlos a un rango de 0 a 1. A este resultado se le llama valor normalizado y se denota CsrgbC_{srgb}.

Csrgb=C8bit255C_{srgb}=\frac{C_{8bit}}{255}

  1. Linealización

Con el valor normalizado, eliminamos la corrección gamma. Para cada canal R, G, B se aplica la siguiente fórmula:

  • Si Csrgb0.04045C_{srgb} \le 0.04045:

Clineal=Csrgb12.92C_{lineal}=\frac{C_{srgb}}{12.92}

  • Si Csrgb>0.04045C_{srgb} > 0.04045

Clineal=(Csrgb+0.0551.055)2.4C_{lineal}=\left(\frac{C_{srgb}+0.055}{1.055}\right)^{2.4}

  1. Aplicar los coeficientes de luminancia

Con los canales en formato lineal, los sumamos aplicando un pesos específicos para cada uno, y obtenemos la luminacia relativa (LL)

L=0.2126Rlineal+0.7152Glineal+0.0722BlinealL=0.2126\cdot R_{lineal}+0.7152\cdot G_{lineal}+0.0722\cdot B_{lineal}

Antes había dicho que el ojo humano percibe la luz de forma no-lineal. En la fórmula anterior se aprecia al mirar los coeficientes de cada canal. El canal GG (verde) es el que tiene más peso. O sea nuestros ojos son mucho más sensibles al verde que al azul, que es el que tiene el coeficiente más bajo.

RGB(255, 0, 0)
RGB(0, 255, 0)
RGB(0, 0, 255)
Comparativa de los tres colores primarios luz (Rojo, Verde y Azul) con sus valores máximos en el modelo RGB.
  1. Evaluación de contraste

Al obtener la luminancia relativa de los dos colores a contrastar (L1L_1 y L2L_2), usamos la siguiente fórmula para determinar el ratio de contraste:

(L1+0.05)/(L2+0.05)\left(L_1 + 0.05)/ \right(L_2 + 0.05)

donde L1L_1 siempre es mayor que L2L_2, no importa cuál es el color del texto, ni cuál es el color del background.

Los resultados irán desde 1:1 (sin contraste, colores idénticos), hasta 21:1 (negro puro versus blanco puro).

Ejemplo:

Tengo el color RGB(209, 71, 71)

Color rojo con valor RGB(209, 71, 71).
  1. Normalizo los canales
Cr=2092550.82C_r=\frac{209}{255}\approx 0.82
Cg=712550.28C_g=\frac{71}{255}\approx 0.28
Cb=712550.28C_b=\frac{71}{255}\approx 0.28
  1. Linealizo los canales normalizados
Cr0.82>0.04045C_r\approx0.82 > 0.04045
 Clinealr(0.82+0.0551.055)2.4\therefore \ C_{lineal_r}\approx\left(\frac{0.82 + 0.055}{1.055}\right)^{2.4}
 Clinealr0.64\therefore \ C_{lineal_r}\approx0.64
Clinealg=Clinealb(0.28+0.0551.055)2.4C_{lineal_g}=C_{lineal_b}\approx\left(\frac{0.28 + 0.055}{1.055}\right)^{2.4}
Clinealg=Clinealb0.064C_{lineal_g}=C_{lineal_b}\approx0.064
  1. Aplico los coeficientes de luminancia
L(0.21260.28)+(0.71520.064)+(0.07220.064)L\approx(0.2126\cdot0.28)+(0.7152\cdot0.064)+(0.0722\cdot0.064)
L0.18L\approx0.18
  1. Evaluó contraste

Voy a comparar el color rojo contra blanco, que tiene una luminancia relativa de 1.

Ratio de contraste(1+0.05)(0.18+0.05)Ratio\ de\ contraste\approx\frac{(1+0.05)}{(0.18+0.05)}
Ratio de contraste4.46Ratio\ de\ contraste\approx4.46

Ese ratio de contraste no alcanza a pasar AA (4.5:1), ni AAA (7:1) para textos normales. Tampoco AAA para textos grandes (4.5:1), pero sí AA para textos grandes (3:1) y aprueba para objetos gráficos y componentes UI (3:1).

Lo que significa que puedo usar esta combinación de colores para texto y background sin generar problemas al usuario siempre que mi texto tenga un tamaño mínimo de 18 puntos (~24px) o 14 puntos en negrita (~19px); o si los uso en íconos, imágenes, gráficos y otros elementos de la interfaz.

Texto en 24pxTexto en 19px y negrita
Resultados de evaluación de contraste
Validación técnica de referencia
  1. Blanco sobre nivel 500
  2. Blanco sobre nivel 600
  3. Blanco sobre nivel 700
  4. Negro sobre nivel 200
  5. Negro sobre nivel 100
Color 1 Color 2 Ratio Texto normal
(AA/AAA)
Texto grande
(AA/AAA)
Componentes UI

Blanco

500

2.05 Falla / Falla Falla / Falla Falla

Blanco

600

3.17 Falla / Falla Falla / Falla Pasa

Blanco

700

4.95 Pasa / Falla Pasa / Pasa Pasa

Negro

200

16.63 Pasa / Pasa Pasa / Pasa Pasa

Negro

100

17.70 Pasa / Pasa Pasa / Pasa Pasa

Con los ratios de contraste se observa que:

  • El nivel 500 de mi paleta no cumple con el estándar mínimo al ser contrastado con el color blanco. Pero al probarlo versus el color negro tiene un ratio excelente (10.23:1).
  • El nivel 600 tampoco tiene un buen ratio contra blanco, y tiene uno bueno contra texto negro (6.61:1), que falla en AAA para texto normal. Al igual que el ejemplo anterior con el color rojo, el nivel 600 de la paleta verde versus color blanco, funciona con textos sobre los 24px o 19px en negrita, y con objetos gráficos y componentes UI.
  • El nivel 700 tiene un buen ratio que falla en AAA para texto normal. Esto significa que, si bien en la práctica es accesible, algunas personas con problemas de visión podrían tener dificultades para leerlo. Con texto negro su calificación es mala, teniendo un ratio de contraste de 4.38:1.
  • El nivel 200 con texto negro pasa sin problemas en todos los estándares.
  • Mismo caso con texto negro sobre nivel 100.

Lo anterior me dice que la paleta funcionaría con texto blanco sobre fondos 700, 800 y 900. Mientras que el texto negro funciona sobre fondos desde el 50 al 600.

Validación práctica

Las cinco combinaciones que se probarán se concentran en los niveles medios-claros y medios-oscuros, siendo el punto de estrés máximo el caso 300 sobre 800.

  1. Nivel 100 sobre nivel 800
  2. Nivel 200 sobre nivel 700
  3. Nivel 200 sobre nivel 900
  4. Nivel 300 sobre nivel 800
  5. Nivel 500 sobre nivel 900
Color 1 Color 2 Ratio Texto normal (AA/AAA) Texto grande (AA/AAA) Componentes UI

100

800

6.24 Pasa / Falla Pasa / Pasa Pasa

200

700

3.92 Falla / Falla Pasa / Falla Pasa

200

900

9.07 Pasa / Pasa Pasa / Pasa Pasa

300

800

5.33 Pasa / Falla Pasa / Pasa Pasa

500

900

5.58 Pasa / Falla Pasa / Pasa Pasa

Se puede observar que todas las pruebas pasan el estándar mínimo (AA) en sus combinaciones, lo que habla de una paleta útil. La prueba de estrés, que era el caso más exigente, pasó sin problemas, pero el nivel 200 contra el 700 falló las pruebas de contraste.

Esto tiene directa relación con algo que ya había mencionado en este artículo: como nuestros ojos perciben el verde más brillante, se comprime la distancia perceptual entre sus niveles medios. Y si bien hay una distancia de 48 puntos porcentuales entre las luminosidades de los valores 200 (L: 80%) y 700 (L: 32%), no es suficiente para que nuestros ojos los diferencien con el suficiente contraste.

Prueba 2: Estados de botón

Probar estados de botón es relevante porque valida que la escala tiene suficiente variedad perceptual para comunicar distintos estados de un componente tan común en interfaces, como lo es un botón, sin salir de la paleta monocromática.

Los parámetros para esta prueba son:

  • Sección con fondo nivel 100,
  • card con fondo nivel 50,
  • botón con fondo 500 y texto blanco,
  • hover state de botón con fondo 600,
  • active state de botón con fondo 700,
  • disabled state con fondo 200 y texto 400, y
  • agregar focus state.

Lo primero que se puede observar, y que ya había comprobado en la prueba anterior, es que texto blanco sobre fondos 500 y 600 no cumplen con el ratio de contraste mínimo de accesibilidad.

Luego, el botón normal y disabled tampoco se diferencian del fondo. El fondo de la card en nivel 50 versus el fondo del botón en nivel 500 tienen un ratio de contraste de 1.81:1, lo que no pasa ninguna prueba de accesibilidad. En el caso del fondo de la card versus el fondo del botón disabled (nivel 200), el ratio de contraste es de 1.11:1.

Por último, el texto del botón disabled (nivel 400) sobre el fondo del mismo, tiene un ratio de contraste de 1.32:1, lo que lo hace ilegible.

Color 1 Color 2 Ratio Texto normal (AA/AAA) Texto grande (AA/AAA) Componentes UI

50

500

1.81 Falla / Falla Falla / Falla Falla

50

200

1.11 Falla / Falla Falla / Falla Falla

200

400

1.32 Falla / Falla Falla / Falla Falla

Para solucionar estos problemas y usando solo colores de la escala, probaré lo siguiente:

  • Cambiar el texto del botón normal a 900,
  • cambiar el texto de los botones on hover, active y on focus a 50,
  • aumentar el peso del texto a semi-bold,
  • cambiar el fondo del botón on hover y on focus a 700,
  • cambiar el botón active a 800,
  • agregar un borde de color 600,
  • cambiar el color del texto del botón disabled a 500.

Se puede concluir que nuestro color base HSL(160°, 60%, 50%) es muy claro para texto blanco, pero funciona con texto color negro, como el nivel 900 de la escala. Una solución sin modificar valores en la escala, es invertir la relación texto/fondo.

El problema encontrado en esta prueba no es una limitación permanente de la escala sino una señal de que el nivel 500 necesita un contexto de fondo suficientemente oscuro para funcionar como componente, lo que quedará demostrado en la Prueba 3.

Prueba 3: Dark mode

Voy a diseñar una sección de suscripción a Newsletter para ver como interactúan diferentes elementos de una interfaz usando la paleta producida. La vuelta es que esta interfaz tiene que ser en dark mode.

Los elementos contenidos serán:

  • Card
  • Input text
  • Botón
  • H1, H3, párrafo
  • Badges

A primera vista parece funcionar, pero se puede mejorar.

  • El badge de la columna izquierda tiene el mismo color que el botón de suscripción, por lo que ambos compiten por atención. El CTA del newsletter es el botón, por lo que es necesario que nada le compita en jerarquía visual. Esto se puede solucionar cambiando el color de fondo del badge por uno más oscuro (800), un borde claro (700) para distinguirlo del fondo de la sección (que es 900); y el texto del badge se puede aclarar, dándole un valor de 100.

Antes

+10k suscriptores se unieron este mes

Después

+10k suscriptores se unieron este mes
  • El texto del párrafo y el texto del placeholder usan el nivel 400 de la escala. Si bien el tamaño del texto y el fondo sobre el que están ya tienen una diferencia de jerarquía, se puede acentuar un poco más, cambiando el color del texto del párrafo a 300, lo que además ayuda a legibilidad por aumento de contraste con el fondo de la sección (Antes: ratio de contraste 400 vs. 900 → 6.82:1; después: 300 vs. 900→ 8.25:1).

Antes

Únete a miles de profesionales que reciben nuestra lista de noticias de la industria, tutoriales avanzados, y recursos exclusivos cada martes. Sin spam, solo valor.

Después

Únete a miles de profesionales que reciben nuestra lista de noticias de la industria, tutoriales avanzados, y recursos exclusivos cada martes. Sin spam, solo valor.

Adicional a estos cambios, podemos agregar un mensaje de éxito para cuando el usuario se suscribe al newsletter.

  • Fondo de mensaje: 400, para que se destaque por sobre todos los demás elementos. Le estoy avisando al usuario que todo salió bien.
  • Texto: 900, mucho contraste para aumentar la jerarquía visual.
  • Peso de la fuente: 700 (bold), más contraste, mayor jerarquía visual.
  • Border radius: 5px, cohesión con el resto de la interfaz.

También quiero agregar un estilo al estado focus del botón de suscripción (en caso de navegación con tab) y modifiqué el estado focus del input también, dejándoles un clásico outline-offset de 2px usando el color 500 de la escala.

Resultado de la prueba

Esta es la versión con las correcciones y adiciones:

+10k suscriptores se unieron este mes

Mantente al día con nuestro Newsletter Semanal

Información de expertos directo en tu bandeja de entrada

Únete a miles de profesionales que reciben nuestra lista de noticias de la industria, tutoriales avanzados, y recursos exclusivos cada martes. Sin spam, solo valor.

Listo, ya estás dentro.
¡Nuevo número disponible!
Ophelia Von Tessen

Editora Jefa

Estos son los ratios de contraste finales:

Color 1 Color 2 Ratio Texto normal (AA/AAA) Texto grande (AA/AAA) Componentes UI
Títulos

H1-100

900

9.66 Pasa / Pasa Pasa / Pasa Pasa

H3-300

900

8.25 Pasa / Pasa Pasa / Pasa Pasa

p-300

900

8.25 Pasa / Pasa Pasa / Pasa Pasa
Badge izquierda

texto-100

800

6.24 Pasa / Pasa Pasa / Pasa Pasa
input text

placeholder-400

800

4.41 Falla (*) / Falla Pasa / Falla Pasa

texto-100

800

6.24 Pasa / Pasa Pasa / Pasa Pasa
Botón

texto-900

500

5.58 Pasa / Pasa Pasa / Pasa Pasa
Botón on hover

texto-900

400

6.82 Pasa / Pasa Pasa / Pasa Pasa
Badge derecha

texto-300

800

5.33 Pasa / Pasa Pasa / Pasa Pasa
Panel de autor

texto-100

800

6.24 Pasa / Pasa Pasa / Pasa Pasa
(*) Ratio de contraste solo un poco por debajo del mínimo estándar AA, pero al ser un placeholder se espera que el texto se vea más muted.
¿Puede la escala producida sostener tanto dark mode como light mode?

Sin hacer ajustes adicionales, y solo invirtiendo los valores (900 → 50; 800 → 100; 700 → 200; 600 → 300; 500 → 400), este es el resultado:

Claramente esta versión necesita ajustes. Simplemente invertir los valores no sirve. Esto porque, como ya se ha mencionado varias veces, la percepción del color no es líneal. Específicamente el verde es perceptualmente más brillante, por lo que los colores claros de la paleta van a brillar más.

Haciendo los siguientes ajustes:

  • Fondo general: 50,
  • Fondo badge izquierda: 300,
  • Texto badge izquierda: 800,
  • Borde badge izquierda: 500,
  • H1: 800,
  • H3 y p: 700,
  • Texto placeholder: 600,
  • Texto input: 800,
  • Borde input/outline focus: 600,
  • Fondo botón: 600,
  • Texto botón: 50,
  • Fondo botón on hover: 700,
  • Fondo mensaje de éxito: 600,
  • Texto mensaje de éxito: 50,
  • Fondo card derecha: 300,
  • Fondo badge derecha: 100,
  • Texto badge derecha: 800,
  • Borde badge derecha: 500,
  • Fondo autor: 100,
  • Borde autor: 500;

el resultado es este:

+10k suscriptores se unieron este mes

Mantente al día con nuestro Newsletter Semanal

Información de expertos directo en tu bandeja de entrada

Únete a miles de profesionales que reciben nuestra lista de noticias de la industria, tutoriales avanzados, y recursos exclusivos cada martes. Sin spam, solo valor.

Listo, ya estás dentro.
¡Nuevo número disponible!
Ophelia Von Tessen

Editora Jefa

Agregando algunos bordes y ajustando los colores, usando la misma escala, la legibilidad mejora.

Conclusiones

Esta prueba demuestra que la escala producida es funcional.

  1. Los colores claros de la paleta funcionan tanto como background en light mode, y como texto en dark mode.
  2. Los colores oscuros también funcionan como background en dark mode, y como texto en light mode.
  3. Los colores medios están algo más apretados, pero haciendo uso de la misma paleta y agregando bordes más claros o más oscuros, se logra crear el contraste necesario.
Implementaciones

Para implementar ambas versiones usando la misma escala, definí cada nivel como una variable CSS en :root y usé data-attributes para alternar entre los temas. Esto me permite usar la misma estructura HTML para ambos modos, cambiando un solo atributo, lo que es la base de cualquier sistema de theming escalable.

:root{
  /* ESCALA 50-900 VERDE */
  --verde-50: hsl(160 80% 90%);
  --verde-100: hsl(160 85% 85%);
  --verde-200: hsl(160 80% 80%);
  --verde-300: hsl(160 70% 75%); 
  --verde-400: hsl(160 60% 65%); 
  --verde-500: hsl(160 60% 50%); 
  --verde-600: hsl(160 60% 40%); 
  --verde-700: hsl(160 55% 32%); 
  --verde-800: hsl(160 50% 25%); 
  --verde-900: hsl(160 40% 18%);     
}

/*-- CONFIGURACIÓN: DARK MODE --*/
[data-tema="dark-verde"] { background-color: var(--verde-900); }
[data-tema="dark-verde"] h1 { color: var(--verde-100); }
[data-tema="dark-verde"] h3 { color: var(--verde-300); }
[data-tema="dark-verde"] p { color: var(--verde-400); }
...

/*-- CONFIGURACIÓN: LIGHT MODE --*/
[data-tema="light-verde"] { background-color: var(--verde-50); }
[data-tema="light-verde"] h1 { color: var(--verde-800); }
[data-tema="light-verde"] h3 { color: var(--verde-700); }
[data-tema="light-verde"] p { color: var(--verde-700); }
...

Dado que el presente post trata sobre escalas monocromáticas, la exploración de sistemas de tokens y arquitectura de theming queda para una futura publicación.

Prueba 4: Overlay

Los colores muy oscuros a veces están tan saturados que una capa de luz encima hace que se vean lavados y pierdan identidad. Así que voy a poner un overlay blanco al 10% sobre mi color 700.

Color normal
Color con overlay

Si bien el color se aclara, no pierde identidad. No se vuelve sucio o grisáceo. Esto confirma que la decisión de reducir la saturación en los niveles 700, 800 y 900 fue correcta. Los colores oscuros de la escala son profundos, pero no hiperactivos.

Conclusión

El propósito de este caso de estudio fue explorar el uso del color en interfaces, específicamente en la construcción de una paleta monocromática funcional para su uso en UI. A lo largo del proceso, trabajé sobre los siguientes aspectos:

  1. Creación manual de escala HSL 50-900.
  2. Validación matemática manual de contrastes.
  3. Uso práctico de los colores en componentes UI + iteración, y diseño adaptativo (dark mode/light mode).
  4. Prueba de resiliencia de los colores oscuros (overlay).

Partí sin conocer lo que es el HSL y salgo de este estudio entendiendo:

  • qué es el matiz,
  • cómo afectan al color la saturación y la luminosidad;
  • que la percepción del ojo no es lineal;
  • que tomando los tres colores principales del sistema RGB, el color verde se percibe más brillante que el rojo, y este más brillante que el azul;
  • por lo tanto la compensación de luminosidad va a variar en la creación de escalas monocromáticas;
  • que en los niveles más claros se compensa la pérdida de color aumentando la saturación, y en los niveles más oscuros se debe equilibrar la saturación y luminosidad para que la identidad del matiz no se pierda, pero no quede un color muy llamativo;
  • que como la percepción de los colores no es lineal, no se puede aumentar/disminuir saturación ni luminosidad de forma pareja (ejemplo: -10/+10 en cada paso) para obtener una escala;
  • por el mismo motivo anterior, tampoco es necesariamente útil simplemente invertir los valores de la escala cuando se quiere adaptar una interfaz a light/dark mode;
  • que una buena forma de crear contraste entre dos elementos con un color de valor contiguo, es agregar un borde que contraste con ambos, creando una separación que se ve limpia.

La paleta producida no es universalmente accesible ni pretende serlo; fue construida para entender las restricciones y compromisos del color en una interfaz.

Este proceso profundizó mi comprensión del comportamiento del color y redefinió el criterio con el que abordo la construcción de paletas para interfaces.