Bhinish Dhiman
Contraste en modo oscuro: por qué WCAG 2 te engaña
Traducido del original en inglés. Leer el original
WCAG 2 suma una constante fija de 0.05 a ambos valores de luminancia. Cerca del negro, esa constante domina el ratio, así que los pares en modo oscuro pueden aprobar con holgura y aun así verse apagados. Revisa los temas oscuros con APCA, y cuenta con necesitar una variante más clara de cada color de marca.
Una paleta que pasa todas las comprobaciones sobre blanco no las pasa automáticamente cuando la inviertes. Peor aún, la herramienta estándar a menudo te dirá que sí las pasó.
De dónde viene el problema
La fórmula de contraste de WCAG 2 es:
(L1 + 0.05) / (L2 + 0.05)
donde L1 y L2 son las luminancias relativas del color más claro y del más oscuro. Ese 0.05 modela el destello ambiental de la pantalla: la luz que se refleja en el monitor y que nunca desaparece del todo, de modo que nada en pantalla tiene una luminancia realmente nula.
Es una simplificación razonable, y en el extremo claro del rango apenas importa. Una luminancia de 0.9 más 0.05 es un ajuste del 5%.
En el extremo oscuro domina por completo. Un gris muy oscuro puede tener una luminancia de 0.008. Sumarle 0.05 lo deja en 0.058: la constante es ahora siete veces mayor que la medición real. La fórmula dejó de describir los colores y empezó a describir la constante.
La consecuencia práctica: las pequeñas diferencias de luminancia entre colores oscuros se inflan hasta parecer ratios grandes. Dos grises oscuros que de verdad cuesta distinguir pueden aprobar con holgura.
Cómo se ve esto en la práctica
El error típico del modo oscuro es tomar un color de marca que funciona sobre blanco y usarlo sin cambios sobre un fondo oscuro.
Toma un azul medio como #2563eb. Sobre blanco da unos 5.17:1, un AA limpio para texto de cuerpo. Ponlo sobre un casi negro #0c0f15 y WCAG 2 da aproximadamente 3.6:1. Por debajo de AA para texto de cuerpo, así que ese lo detectarías.
Pero acláralo un poco y puedes llegar a 4.5:1 mientras el texto sigue viéndose apagado y con una ligera vibración sobre el fondo oscuro. El ratio dice que aprueba. Tus ojos dicen otra cosa. Y sobre un fondo oscuro, la penalización perceptual por quedarse en el límite es mucho más dura que sobre blanco.
Nos pasó exactamente esto al construir nuestro propio sitio. El color de los enlaces en modo oscuro pasaba WCAG AA con holgura, y cuando lo pasamos por APCA dio Lc 37 frente a un requisito de 75. En una captura de pantalla se veía bien y en realidad era difícil de leer.
APCA maneja bien la polaridad
APCA produce un valor con signo. Positivo significa texto oscuro sobre fondo claro; negativo, texto claro sobre fondo oscuro. Usa curvas distintas para cada dirección, porque la respuesta perceptual es de verdad asimétrica: el texto claro sobre un fondo oscuro parece expandirse hacia afuera y ganar peso aparente, un efecto que no ocurre a la inversa.
Por eso APCA es la cifra más fiable específicamente en modo oscuro. Se diseñó teniendo en cuenta esa asimetría, mientras que WCAG 2 trata ambas direcciones con la misma aritmética.
Qué hacer
Dale a cada color de marca dos variantes. Una oscurecida para fondos claros y otra aclarada para fondos oscuros. Mismo tono, mismo croma, dos valores de luminosidad. Es el cambio estructural más útil, y es lo que de verdad exige publicar en ambos temas. Un solo color no puede servir para los dos.
No uses negro puro como fondo oscuro. #000000 maximiza la distorsión del destello y produce un halo incómodo con el texto claro. Algo en el rango de #0c0f15 a #16181d es más fácil de leer y te deja espacio para construir una escala de elevación.
Tampoco uses texto blanco puro. El blanco a máximo brillo sobre casi negro es duro y provoca un resplandor visible en tamaños pequeños. Un blanco roto ligeramente cálido, en la zona de #e8ecf2, se lee mejor y casi no cambia la medición.
Reduce la saturación a medida que aclaras. Los colores muy saturados vibran sobre fondos oscuros. Bajar el croma al subir la luminosidad produce un resultado más tranquilo que mide lo mismo.
Revisa el modo oscuro al tamaño y peso reales. La diferencia entre los dos modelos crece a medida que el texto se hace más pequeño, así que una etiqueta de 14px en modo oscuro es el peor caso en ambos ejes a la vez.
Cómo acertar con las variantes
El método eficiente es fijar el tono y el croma y mover solo la luminosidad, porque la luminosidad es lo que determina el contraste y el tono es lo que lleva tu marca. Trabaja en OKLCH, donde la luminosidad es perceptualmente uniforme, así que los pasos iguales se ven iguales.
Eso te da un patrón de dos tokens:
--brand: oklch(55% 0.16 255);
--brand-on-light: oklch(48% 0.16 255);
--brand-on-dark: oklch(78% 0.12 255);
Fíjate en la bajada de croma en la variante oscura. Es el punto sobre la desaturación de arriba, llevado a la práctica.
Nuestro verificador de contraste muestra WCAG 2 y APCA juntos y avisa cuando no coinciden, algo que sobre fondos oscuros pasa a menudo. La pista de luminosidad muestra qué tramos de tu tono aprueban frente a un fondo dado, así que puedes elegir ambas variantes del mismo eje.