Utilice otras funciones de CSS para aproximar colores contrastantes()
Tienes un elemento con un color de fondo configurable y quieres calcular si el texto de primer plano debe ser claro u oscuro. Parece bastante sencillo, sobre todo sabiendo cómo debemos prestar atención a la accesibilidad.
Ya existen algunas especificaciones preliminares para esta característica y, recientemente, contrast-color() (antes color-contrast())existir Borrador de nivel 5 del módulo de color CSS. Pero dado que Safari y Firefox son los únicos navegadores que implementan esta función hasta ahora, es posible que la versión final de esta función aún esté muy lejos. Mientras tanto, se han agregado muchas funciones a CSS; Lo suficiente como para que hoy quisiera ver si podíamos implementarlo de manera compatible con todos los navegadores. Esto es lo que tengo:
color: oklch(from <your color> round(1.21 - L) 0 0);
Déjame explicarte cómo llegué aquí.
Asociación Mundial para la Protección de la Infancia 2.2
WCAG proporciona fórmulas para calcular el contraste entre dos colores RGB Detalles de Stacie Arellano. Se basa en el antiguo método de cálculo. brillo Colores (qué tan brillantes se ven) e incluso limitaciones al tratar de limitar el brillo del monitor y la pantalla:
L1 + 0.05 / L2 + 0.05
…donde el color más claro (L1) en la parte superior. El brillo varía de 0 a 1, lo que da como resultado una relación de contraste de 1 (1,05/1,05) a 21 (1,05/0,05).
La fórmula para calcular el brillo del color RGB es más confusa, pero solo estoy tratando de determinar si el blanco o el negro tienen un mayor contraste con un color determinado, y se puede simplificar un poco. Terminamos con esto:
L = 0.1910(R/255+0.055)^2.4 + 0.6426(G/255+0.055)^2.4 + 0.0649(B/255+0.055)^2.4
cual de nosotros capaz Convertido a CSS de la siguiente manera:
calc(.1910*pow(r/255 + .055,2.4)+.6426*pow(g/255 + .055,2.4)+.0649*pow(b/255 + .055,2.4))
Podemos redondear todo a 1 o 0 usando round()1 significa blanco, 0 significa negro:
round(.67913 - .1910*pow(r/255 + .055, 2.4) - .6426*pow(g/255 + .055, 2.4) - .0649*pow(b/255 + .055, 2.4))
Multipliquemos esto por 255 y usémoslo para los tres canales: sintaxis de color relativa. Terminamos con esto:
color: rgb(from <your color>
round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)
round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)
round(173.178 - 48.705*pow(r/255 + .055, 2.4) - 163.863*pow(g/255 + .055, 2.4) - 16.5495*pow(b/255 + .055, 2.4), 255)
);
Una fórmula que, dado un color, devuelve blanco o negro según WCAG 2. No es fácil de leer, pero funciona… excepto asociación de asia pacífico Sí Prepárese para reemplazarlo con una fórmula mejor y más nueva en una futura guía WCAG. Podemos volver a hacer los cálculos, aunque APCA es una fórmula más complicada. Podríamos usar funciones CSS para limpiarlo un poco, pero eventualmente la implementación se volvería inaccesible, difícil de leer y de mantener.
nuevo método
Di un paso atrás y pensé en qué más estaba disponible para nosotros. Tenemos otra característica nueva para probar: espacio de color. este”L*“Los valores en el espacio de color CIELAB representan brillo percibido. Está diseñado para reflejar lo que ven nuestros ojos. No es lo mismo que el brillo, pero se acerca. Tal vez podamos adivinar, según el brillo percibido, si debemos usar blanco o negro para obtener un mejor contraste; veamos si podemos encontrar un número en el que para colores con menor brillo usemos negro y para colores con mayor brillo usemos blanco.
Podrías pensar instintivamente que debería ser 50% o 0,5, pero no es así. Muchos colores, incluso si son brillantes, contrastan mejor con el blanco que con el negro. A continuación se muestran algunos ejemplos de uso. lch()aumentando lentamente el brillo manteniendo el mismo tono:
El punto de transición donde el texto negro es más fácil de leer que el texto blanco suele ocurrir entre 60 y 65 años. Así que preparé una aplicación Node rápida usando Colorjs.io Calcule la posición de corte y calcule el contraste usando APCA.
para oklch()encontré que el umbral oscila entre 0,65 y 0,72, con un promedio de 0,69.
en otras palabras:
- Cuando el brillo de OKLCH es 0,72 o superior, el negro siempre tiene mejor contraste que el blanco.
- Por debajo de 0,65, el blanco siempre tendrá mejor contraste que el negro.
- Entre 0,65 y 0,72, normalmente las relaciones de contraste en blanco y negro están entre 45 y 60.
Entonces, solo usa round() y un límite superior de 0,72, podemos hacer una implementación nueva y más corta:
color: oklch(from <your color> round(1.21 - L) 0 0);
Si se pregunta de dónde viene 1,21, 0,72 se redondea hacia abajo y 0,71 se redondea hacia arriba: 1.21 - .72 = .49 redondear hacia abajo, y 1.21 - .71 = .5 redondeo.
Esta fórmula funciona tan bien que se han puesto en producción varias iteraciones de esta fórmula. Es más fácil de leer y mantener. Es decir, esta fórmula es más consistente con la APCA que con las WCAG, por lo que a veces es inconsistente con las WCAG. Por ejemplo, WCAG indica que el negro tiene una relación de contraste más alta cuando se coloca (4,70 frente a 4,3 para el blanco) #407ac2mientras que APCA dice lo contrario: la relación de contraste para el negro es 33,9 y la relación de contraste para el blanco es 75,7. La nueva fórmula CSS coincide con APCA y se muestra en blanco:

Podría decirse que esta fórmula puede funcionar mejor que las WCAG 2.0 porque está más cerca de APCA. Dicho esto, aún debe verificar la accesibilidad, y si cumple legalmente con las WCAG pero no con la APCA, entonces tal vez esta fórmula más nueva y simple no lo ayude mucho.
LCH y OKLCH
Realicé un análisis de datos en ambos y, además de que OKLCH fue diseñado para ser una mejor alternativa a LCH, descubrí que los datos sugerían que OKLCH era la mejor opción.
Con LCH, la brecha entre los negros demasiado oscuros y los blancos demasiado brillantes es generalmente mayor y las brechas se mueven más. Por ejemplo, #e862e5 aprobar #fd76f9 Demasiado oscuro para el negro, demasiado claro para el blanco. Para LCH, el brillo está entre 63 y 70; para OKLCH, el valor es de 0,7 a 0,77. La escala de brillo de OKLCH está más en línea con APCA.
ir más lejos
Si bien el “Contraste máximo” es ciertamente mejor, hay otro truco que podemos implementar. Nuestra lógica actual simplemente nos da blanco o negro (que es este color-contrast() Actualmente la funcionalidad está limitada a), pero podemos cambiarlo para dar blanco u otro color determinado. Por ejemplo, color de texto blanco o básico. Comience con esto:
color: oklch(from <your color> round(1.21 - L) 0 0);
/* becomes: */
--white-or-black: oklch(from <your color> round(1.21 - L) 0 0);
color: rgb(
from color-mix(in srgb, var(--white-or-black), <base color>)
calc(2*r) calc(2*g) calc(2*b)
);
Estas son algunas matemáticas inteligentes, pero no agradables de leer:
- si
--white-or-blackes blanco,color-mix()resultadorgb(127.5, 127.5, 127.5)o más brillante; dos veces más brillantes de lo que somos ahorargb(255, 255, 255)O superior, que es simplemente blanco. - si
--white-or-blackes negro,color-mix()Reduzca el valor de cada canal RGB en un 50%; después de duplicarlo volvemos al valor original<base color>.
Desafortunadamente, esta fórmula no funciona en Safari 18 y versiones anteriores, por lo que deberás apuntar a Chrome, Safari 18+ y Firefox. Sin embargo, nos brinda una manera de cambiar entre blanco y color de texto base usando CSS puro, en lugar de solo blanco y negro, y podemos volver a usar blanco y negro en Safari <18.
También puedes anularlos usando el siguiente comando Funciones personalizadas CSSpero estos no son compatibles en todas partes:
@function --white-black(--color) {
result: oklch(from var(--color) round(1.21 - l) 0 0);
}
@function --white-or-base(--color, --base) {
result: rgb(from color-mix(in srgb, --white-black(var(--color)), var(--base)) calc(2*r) calc(2*g) calc(2*b));
}
en conclusión
Espero que esta técnica funcione bien para usted y me gustaría reiterar que el objetivo de este enfoque (encontrar umbrales y fórmulas simples) es hacer que la implementación sea flexible y fácil de adaptar a sus necesidades. Puede ajustar fácilmente el umbral al valor que mejor se adapte a sus necesidades.