Los esquemas de color son geometría sobre una rueda — y la rueda está mal
Publicado el 15/8/2025 · 16 min de lectura · Herramientas para desarrolladores
Daniel Okonkwo — Desarrollador front-end y redactor de Tecnología en Allin
Rendimiento web · Formatos de archivo
Verificado con 6 fuentes
Un esquema de color es un ángulo. Toma el tono de un color base, súmale un número fijo de grados módulo 360 y ya tienes el esquema: el complementario suma 180°, la tríada suma 120° dos veces, el análogo suma ±30°, el complementario dividido suma 150° y 210°, el tetrádico suma 90° tres veces. Desde el tono 210 eso da 30 para el complementario, 330 y 90 para la tríada, 180 y 240 para los vecinos, 0 y 60 para el dividido. Ese es todo el cálculo, y lo hace cualquier generador. El problema es el círculo sobre el que gira. El eje de tono de HSL es una construcción geométrica sobre el cubo RGB, no un modelo de la visión: los ángulos iguales no son pasos perceptuales iguales, y una luminosidad HSL igual no es una claridad percibida igual. Renderizados como hsl(H 100% 50%), el amarillo tiene una luminancia relativa WCAG de 0,9278 y el azul 0,0722 — la misma luminosidad nominal, 12,85 veces la luz. OKLCH y CIELAB lo arreglan definiendo la claridad de forma perceptual: el mismo par es 0,968 frente a 0,452 en OKLCH, una razón de 2,14. La consecuencia práctica es inevitable: la rotación no preserva nada del contraste, así que una paleta generada debe comprobarse contra sus fondos antes de publicarse.
Complementario, tríada, análogo y complementario dividido no son más que rotaciones: suma 180°, 120°, 30° o 150° a un tono. La aritmética es trivial. El problema es que el círculo de tonos HSL no es perceptualmente uniforme — el amarillo y el azul con la misma luminosidad HSL difieren 12,85 veces en luminancia — así que una paleta generada hay que comprobarla luego en contraste.
Los cuatro esquemas son cuatro sumas
Toda armonía de color con nombre es un conjunto de desplazamientos aplicados a un ángulo de tono. El complementario es base + 180. La tríada es base, base + 120, base + 240. El análogo es base − 30, base, base + 30, aunque algunas herramientas usan 20 o 45. El complementario dividido es base + 150 y base + 210, es decir el complementario separado 30° hacia cada lado. El tetrádico, también llamado cuadrado, es base + 90, + 180 y + 270. El monocromático no cambia ningún tono y sólo varía saturación y luminosidad.
Trabajemos un ejemplo. Toma un azul medio en el tono 210. El complementario da (210 + 180) mod 360 = 30, un naranja. La tríada da 210, (210 + 120) mod 360 = 330 y (210 + 240) mod 360 = 90 — azul, rosa y verde amarillento. El análogo da 180, 210, 240 — cian, azul, azul violáceo. El complementario dividido da (210 + 150) mod 360 = 0 y (210 + 210) mod 360 = 60 — rojo y amarillo. El tetrádico da 210, 300, 30, 120. El algoritmo no tiene nada más; el módulo es la única sutileza, y existe sólo porque el círculo se cierra.
Como los desplazamientos tocan sólo el tono, la saturación y la luminosidad se arrastran sin cambios. Eso es justo lo que da a la salida su aire de familia — y justo lo que provoca el fallo descrito más abajo, porque mantener S y L constantes no mantiene constante nada de lo que miden el ojo humano o un comprobador de accesibilidad.
Hay dos ruedas, y no coinciden sobre el azul
Pregunta a un pintor qué hay enfrente del azul y responderá naranja. Pregúntaselo a un generador de esquemas y responderá amarillo. Ambos tienen razón, porque giran sobre círculos distintos. La rueda del pintor tiene rojo, amarillo y azul como primarios — el modelo RYB, sistematizado por Itten y enseñado desde entonces en las escuelas de arte — y describe la mezcla sustractiva de pigmentos. La rueda del generador es el eje de tono de HSL, una reparametrización cilíndrica del cubo RGB aditivo, cuyos primarios son rojo, verde y azul y cuyos secundarios son cian, magenta y amarillo.
Los números concretan la fractura. Al proyectar el cubo RYB sobre sRGB con la interpolación trilineal clásica, el azul primario RYB cae cerca de #2a5f99 y el naranja RYB en #ff8000, y ocupan esquinas opuestas de ese modelo. En la rueda HSL, el azul es el tono 240 y 240 + 180 = 60, que se renderiza como #ffff00, amarillo puro. Ninguna rueda miente. RYB describe lo que ocurre al mezclar pintura; HSL describe lo que ocurre al mezclar luz. Son modelos de procesos físicos distintos y nada obliga a que sus opuestos coincidan.
Conviene saberlo antes de discutir con la salida de un generador. Si una referencia de diseño te dice que el complemento de tu azul de marca es un naranja cálido y la herramienta te entrega un amarillo limón, nadie se ha equivocado — simplemente estás leyendo dos sistemas de coordenadas. Si quieres la respuesta pictórica, algunos generadores ofrecen un modo RYB; si no, gira entre 30° y 40° menos que el complementario matemático y aterrizas en la zona naranja.
Una luminosidad HSL igual no es luz igual
Aquí está la demostración. Renderiza nueve tonos exactamente como hsl(H 100% 50%) — saturación idéntica, luminosidad nominal idéntica — y pasa cada uno por el cálculo de luminancia relativa de la WCAG: divide cada canal entre 255, linealízalo y pondera los resultados 0,2126 rojo, 0,7152 verde, 0,0722 azul. El tono 60 da #ffff00 con una luminancia relativa de 0,9278. El tono 240 da #0000ff con 0,0722. El mismo deslizador de luminosidad, 12,85 veces la luz.
Las consecuencias se ven de inmediato en términos de contraste. Sobre fondo blanco, ese amarillo saca 1,07:1 — invisible — mientras que ese azul saca 8,59:1 y supera todos los niveles WCAG. Uno contra otro, el par saca 8,00:1, y por eso el amarillo sobre azul es una combinación clásica de señalética y el amarillo sobre blanco un error clásico. El barrido completo es instructivo: el tono 0 (#ff0000) tiene luminancia 0,2126, el tono 120 (#00ff00) 0,7152 y el tono 180 (#00ffff) 0,7874. La dispersión en un solo anillo de luminosidad cubre casi todo el rango de 0 a 1.
La razón está en los pesos. La luminancia relativa está dominada por el verde, que aporta el 71,52 %, mientras que el azul aporta el 7,22 %. La luminosidad de HSL no sabe nada de eso: se define como el punto medio entre el canal RGB mayor y el menor, una magnitud puramente geométrica. Con L = 50 % y S = 100 %, todo tono es por construcción un color cuyos canales cubren todo el rango de 0 a 255, y cuánta luz representa eso depende enteramente de qué canales estén encendidos.
Qué entienden OKLCH y CIELAB por claridad
CIELAB, publicado por la CIE en 1976, fue el primer intento ampliamente adoptado de espacio perceptualmente uniforme. Su eje L* va de 0 a 100 y se construye con una compresión en raíz cúbica de la luminancia, elegida para que pasos numéricos iguales parezcan pasos iguales. Pasa por él el mismo amarillo y el mismo azul: L* = 97,14 para #ffff00 y 32,30 para #0000ff. Siguen siendo muy distintos, pero ahora la diferencia es honesta — el amarillo de verdad parece mucho más claro — y la razón ha caído de 12,85 a 3,01.
OKLab, publicado por Björn Ottosson en 2020 y ya parte de CSS Color 4 como oklab() y oklch(), mejora a CIELAB justo donde CIELAB es notoriamente débil: la constancia de tono en los azules y la previsibilidad al cambiar la claridad. Su L va de 0 a 1. El amarillo vale 0,968 y el azul 0,452. Su forma polar, OKLCH, te da claridad, croma y tono — los mismos tres conceptos que HSL dice ofrecer, pero con los dos primeros significando lo que dicen. Esto importa porque una paleta generada a claridad OKLCH fija parece de verdad una familia de colores con el mismo brillo, que es exactamente lo que los diseñadores creían pedirle a HSL.
Conviene decir con claridad una salvedad. La claridad perceptual sigue sin ser la luminancia relativa, y ni la L de OKLCH ni la L* de CIELAB son lo que calcula un comprobador WCAG. Dos colores con idéntica claridad OKLCH pueden tener luminancias relativas distintas, así que igualar la claridad de forma perceptual no iguala las relaciones de contraste. Consigues una paleta que parece coherente; no consigues una paleta que apruebe.
Rotaciones iguales no son pasos iguales
El problema de la claridad tiene un gemelo en el eje de tono. Rotar 30° en la zona verde — del tono 90 al 120 — te lleva entre dos verdes que la mayoría de la gente apenas sabría distinguir. Rotar 30° en la zona azul-magenta — de 240 a 270 — te lleva del azul puro a un violeta claramente distinto. Los ángulos son idénticos; las distancias percibidas no, porque el tono de HSL deriva de las aristas del cubo RGB y el cubo no es una esfera perceptual.
Mira lo que le hace a una tríada. Rotar 120° desde el tono 0 da #ff0000, #00ff00 y #0000ff, con luminancias relativas 0,2126, 0,7152 y 0,0722 y claridades OKLCH 0,628, 0,866 y 0,452. Un miembro es casi el doble de claro que otro en términos perceptuales y diez veces más en términos físicos. Rota la misma tríada desde el tono 90 y obtienes #80ff00, #0080ff y #ff0080, con claridades OKLCH 0,891, 0,615 y 0,645 — un equilibrio completamente distinto, de la misma construcción, cambiando sólo el ángulo de partida.
Por eso una tríada a veces parece equilibrada y a veces parece un color gritando por encima de otros dos, sin cambiar el método. El método es ciego a eso. Generar el mismo esquema en OKLCH — rotar el tono, mantener L y C — elimina casi toda la varianza, porque la claridad que mantienes es ahora la que ves.
La geometría no sabe nada del contraste
Toma un azul de marca real, #2563eb. En HSL es hsl(221.2 83.2% 53.3%) y sobre blanco saca 5,17:1 — holgadamente por encima del umbral de 4,5:1 para texto de cuerpo en WCAG AA. Ahora genera su familia como se hace siempre, rotando el tono con saturación y luminosidad fijas. El compañero de tríada a +120° es #eb2563 y saca 4,22:1: suspenso. El complementario dividido a +150° es #eb4a25, 3,79:1: suspenso. El complementario a +180° es #ebad25, 1,99:1: suspenso. A +210° es #c6eb25, 1,37:1: suspenso. A +240°, #63eb25, 1,56:1: suspenso. Cinco rotaciones, cinco suspensos, desde una base que aprueba.
No ha fallado nada. El generador hizo exactamente lo que anuncia: rotó un tono. El contraste es una propiedad de un par de colores, y a un generador de esquemas se le entrega un color y un ángulo. No tiene fondo con el que comparar ni forma de saber cuál de sus salidas acabará como texto y cuál como relleno decorativo. Esperar una paleta accesible de una rotación es esperar respuesta a una pregunta que nunca se hizo.
El proceso que funciona tiene dos pasos, no uno. Genera la geometría para encontrar tonos que peguen entre sí y luego ajusta la claridad color a color hasta que cada uno supere la relación que necesita frente a la superficie donde irá. En la práctica eso significa conservar el tono y el croma que dio el generador y mover sólo la claridad — justo la operación que OKLCH hace predecible y HSL no. Los roles de texto exigen 4,5:1, el texto grande y los bordes de interfaz 3:1, y los rellenos decorativos nada en absoluto, así que la mayoría de las paletas acaban con dos variantes de claridad por tono en vez de una.
| Tono | sRGB | Luminancia relativa WCAG | Contraste sobre blanco | L OKLCH | L* CIELAB |
|---|---|---|---|---|---|
| 60 (amarillo) | #ffff00 | 0,9278 | 1,07:1 | 0,968 | 97,14 |
| 180 (cian) | #00ffff | 0,7874 | 1,25:1 | 0,905 | 91,11 |
| 120 (verde) | #00ff00 | 0,7152 | 1,37:1 | 0,866 | 87,73 |
| 30 (naranja) | #ff8000 | 0,3670 | 2,52:1 | 0,732 | 67,05 |
| 300 (magenta) | #ff00ff | 0,2848 | 3,14:1 | 0,702 | 60,32 |
| 0 (rojo) | #ff0000 | 0,2126 | 4,00:1 | 0,628 | 53,24 |
| 270 (violeta) | #8000ff | 0,1181 | 6,25:1 | 0,531 | 40,91 |
| 240 (azul) | #0000ff | 0,0722 | 8,59:1 | 0,452 | 32,30 |
Preguntas frecuentes
- ¿Por qué mi paleta de tríada parece desequilibrada si las matemáticas son simétricas?
- Porque la simetría está en los ángulos, no en la apariencia. Una tríada rota 120° dos veces, algo perfectamente regular en el círculo de tonos, pero ese círculo no es perceptualmente regular. Desde el tono 0 en hsl(H 100% 50%) se obtiene #ff0000, #00ff00 y #0000ff con luminancias relativas 0,2126, 0,7152 y 0,0722 — el verde emite diez veces la luz del azul con idéntica luminosidad HSL. En OKLCH las claridades son 0,628, 0,866 y 0,452, todavía una dispersión de casi dos a uno. El miembro que caiga en el arco verde o amarillo dominará, porque esos tonos llevan casi toda la luminancia en sRGB. El remedio no es abandonar la tríada sino igualar después: convierte los tres a OKLCH, ponles la misma L y vuelve a renderizar. Los tonos siguen donde los puso la geometría y el peso visual se equilibra. Si tu herramienta no puede hacerlo, ajustar la luminosidad HSL a ojo color por color te lleva casi todo el camino — sólo que no puedes hacerlo manteniendo un número constante.
- ¿El complemento del azul es naranja o amarillo?
- Ambos, según en qué rueda estés. En la rueda RYB de los artistas — rojo, amarillo y azul como primarios, el modelo que sistematizó Itten y que las escuelas de arte siguen enseñando — el azul está enfrente del naranja, porque el naranja es lo que sale al mezclar los otros dos primarios. En la rueda RGB que expone HSL, el azul es el tono 240, su opuesto es el tono 60, y el tono 60 a plena saturación es #ffff00, amarillo puro. Proyectar el cubo RYB sobre sRGB coloca el azul RYB cerca de #2a5f99 y el naranja RYB en #ff8000, y son realmente esquinas opuestas de ese modelo. Ninguno se equivoca. RYB modela la mezcla sustractiva de pigmentos, donde azul y naranja se cancelan hacia un marrón neutro; RGB modela la mezcla aditiva de luz, donde luz azul y luz amarilla suman blanco. El software trabaja con luz, así que el complementario de un generador será el amarillo. Si quieres el resultado pictórico, usa una herramienta con modo RYB o rota entre 30° y 40° menos que el complementario matemático — desde el tono 240 aterrizas hacia el tono 30, que se renderiza como #ff8000, el naranja que esperabas.
- ¿Debo generar paletas en OKLCH en lugar de HSL?
- Para cualquier caso en que los miembros deban sentirse como iguales, sí. Mantener constante la claridad OKLCH a lo largo de una rotación da colores que de verdad parecen igual de claros, mientras que mantener constante la luminosidad HSL da la dispersión de luminancia de 12,85 veces entre amarillo y azul descrita arriba. OKLCH está soportado en CSS como oklch() y forma parte de la especificación CSS Color 4, así que puedes escribirlo directamente en vez de convertir. Dos cautelas prácticas. Primero, OKLCH describe colores fuera del gamut sRGB, así que un croma que funciona en un tono puede ser inalcanzable en otro — los navegadores hacen gamut mapping, pero el resultado tendrá menos croma del pedido, y una paleta definida con un único C alto no saldrá uniformemente saturada. Mantén C conservador, entre 0,10 y 0,15, si necesitas que todos los tonos sobrevivan. Segundo, la claridad OKLCH sigue sin ser la luminancia relativa de la WCAG, así que paletas de L igual no son paletas de contraste igual. Sigues teniendo que comprobar cada color contra cada fondo que vaya a encontrar. Lo que te da OKLCH es que la comprobación normalmente aprobará o suspenderá para todos los miembros a la vez, en lugar de uno a uno por razones que nadie sabe explicar.
- ¿Una rotación de tono mayor significa siempre más contraste?
- No, y los contraejemplos son fáciles de producir. El contraste según la WCAG depende sólo de la luminancia relativa, y la rotación de tono cambia la luminancia de un modo que no tiene nada que ver con cuánto rotaste. Desde #2563eb la rotación que menos daña el contraste es +120° con 4,22:1, y la que más lo daña es +210° con 1,37:1 — pero +180°, la mayor rotación posible, aterriza en 1,99:1, entre ambas. La relación no es monótona porque los pesos de luminancia alcanzan un pico brusco en el arco verde-amarillo: cualquier rotación que te lleve a la región de los tonos 60 a 120 subirá la luminancia y, sobre fondo blanco, bajará el contraste, tanto si llegaste girando 90° como 250°. El modelo mental útil: la rotación de tono es un paseo por un relieve cuya altura es la luminancia, con una montaña hacia el verde-amarillo y un valle hacia el azul. La distancia recorrida no dice nada de la altitud alcanzada. Si dos colores deben contrastar, separa deliberadamente sus claridades; rotar el tono por sí solo no es una estrategia de contraste, es una estrategia de tono.
- ¿Cuántos colores necesita realmente un esquema?
- Menos tonos y más escalones de claridad de los que sugiere la mayoría de los generadores. Una paleta de interfaz que funciona suele ser un tono de marca, una rampa neutra y como mucho un acento, cada uno existiendo en varias variantes de claridad — un tinte claro de superficie, un relleno medio y una versión oscura apta para texto. Esta última explica por qué crece la cuenta: como se mostró arriba, un mismo tono con una misma claridad no puede servir a la vez de fondo lavado y de texto legible, porque uno de esos roles exige 4,5:1 frente al blanco y el otro debe ser apenas visible sobre él. El trabajo del generador es encontrar las relaciones de tono; el tuyo es dar a cada tono los dos o tres escalones de claridad que sus roles piden. Un esquema de cuatro tonos con una variante cada uno fallará en algún sitio; uno de dos tonos con cinco variantes cada uno, no. Si estás contando, apunta a una rampa neutra de nueve escalones, cinco escalones del tono de marca y cinco del acento, y comprueba los destinados a texto frente a todas las superficies donde puedan aparecer.
Artículos que podrían interesarte
Todas las guías →Herramientas relacionadas
Fuentes
- W3C — CSS Color Module Level 4 — hue interpolation, oklab() and oklch(), and the HSL to RGB algorithm
- W3C — Web Content Accessibility Guidelines (WCAG) 2.2 — relative luminance and contrast ratio definitions
- CIE — CIE 015:2018 Colorimetry, 4th Edition — CIELAB and the L* lightness function
- Björn Ottosson — A perceptual color space for image processing (OKLab), 2020
- Charles Poynton — Color FAQ and Gamma FAQ — luminance, lightness and the difference between them
- IEC — IEC 61966-2-1:1999 — Default RGB colour space (sRGB), source of the piecewise transfer function
¿Has detectado un error en este artículo?