El Problema de los Tres Colores


Me han presentado un problema de diseño que me gustaría compartir por su resultado sorprendentemente poco intuitivo.

Cuando diseñamos el estilo de una web accesible, el contraste de los elementos que la forman es uno de los principales puntos a tener en cuenta. No hay que ser experto en óptica para darse cuenta de que si queremos maximizar el contraste de un fondo blanco debemos colorear el texto en negro y viceversa. El problema planteado era hallar el color de fondo que maximice el contraste para un texto blanco y negro, simultáneamente.

En un primer momento me decanté por el gris #808080 por ser el color central del cubo RGB y del cilindro HSL. Esta respuesta geométrica es la más intuitiva pero errónea. La representación del color en esos espacios está totalmente manipulada para que pueda usarse más fácilmente, pero en realidad, la percepción del color es no lineal por lo que todas las representaciones simétricas no captan de forma consistente las diferencias entre tonos.

Afortunadamente, me di cuenta de mi error e intenté calcular el óptimo. Como decía Ken Thompson: “en caso de duda, usar la fuerza bruta”.

F = 0.01
D = range(0,255)

def X_8bit2sRGB(X_8bit):
    X_sRGB = X_8bit/255
    if X_sRGB <= 0.03928:
        X = X_sRGB/12.92
    else:
        X = ((X_sRGB+0.055)/1.055)**2.4
    return X    

for R_8bit in D:
    R = X_8bit2sRGB(R_8bit)
    for G_8bit in D:
        G = X_8bit2sRGB(G_8bit)
        for B_8bit in D:
            B = X_8bit2sRGB(B_8bit)

            L = 0.2126*R+0.7152*G+0.0722*B
            C_B = (L+0.05)/0.05
            C_W = 1.05/(L+0.05)
            
            if C_B >= 4.5 and C_W >= 4.5:
                C = abs(C_B-C_W)
                if C <= F:
                    F = C
                    print(f"#{R_8bit:02X}{G_8bit:02X}{B_8bit:02X}")

Este script en Python que programé en dos minutos en un trozo de papel recorre todo el cubo RGB, los 16.777.216 tonos, comparando su contraste con los colores blanco y negro, seleccionando cada vez los mejores. Las matemáticas detrás de este desarrollo se encuentran recogidas en la guía de WCAG 2.0. No voy a comentarlas porque no entiendo nada de teoría del color y solo me he limitado a implementarlas en este pequeño script.

El resultado es totalmente inesperado y en cierta forma decepcionante.

#CF0DCC: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#CF0DCC: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

El color #CF0DCC es el óptimo, aunque un color magenta como este es horroroso y no podría usarlo en ningún diseño. Sin embargo, el script también proporciona soluciones subóptimas, con resultados prácticamente iguales pero tonos mucho más útiles como son los siguientes, que he seleccionando entre todos.

#EC0408: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#EC0408: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#038901: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#038901: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#006DFB: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#006DFB: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#6562F2: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua

#6562F2: Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua