Ir al contenido
OneKitly

Relación de contraste: cómo la calcula realmente la WCAG

Publicado el 15/5/2025 · 17 min de lectura · Herramientas para desarrolladores

Daniel Okonkwo

Daniel OkonkwoDesarrollador front-end y redactor de Tecnología en OneKitly

Rendimiento web · Formatos de archivo

Verificado con 6 fuentes

Ver perfil
En resumen

La WCAG 2.x define el contraste como (L1 + 0,05) ÷ (L2 + 0,05), donde L1 es la luminancia relativa del color más claro y L2 la del más oscuro. La luminancia relativa no es el brillo ni la L de HSL. Cada canal sRGB se divide entre 255 y luego se linealiza con una función por tramos — por debajo de 0,03928 se divide entre 12,92, en caso contrario se eleva ((c + 0,055) ÷ 1,055) a 2,4 — y los tres valores lineales se ponderan 0,2126 rojo, 0,7152 verde, 0,0722 azul. El verde aporta el 71,52 % de la luminancia y el azul solo el 7,22 %, lo que produce el resultado que nadie espera: el azul puro #0000ff sobre blanco saca 8,59:1 y supera todos los niveles WCAG, mientras que el gris medio #808080 sobre blanco saca 3,95:1 y suspende para texto normal. El término 0,05 es la razón de que el negro sobre blanco sea 21:1 y no infinito: el negro tiene luminancia 0, y 1,05 ÷ 0,05 = 21. Los umbrales son 4,5:1 para texto de cuerpo en AA, 3:1 para texto grande (18 pt = 24 px CSS, o 14 pt en negrita = 18,67 px) y para elementos no textuales, y 7:1 en AAA. La WCAG 3 es un borrador de trabajo temprano que propone otro modelo, perceptual, llamado APCA; hoy no es normativo.

La relación WCAG es (L1 + 0,05) ÷ (L2 + 0,05), y L es la luminancia relativa, no el brillo. El verde aporta el 71,52 % y el azul el 7,22 %, por eso el azul puro sobre blanco aprueba con 8,59:1 mientras el gris medio suspende con 3,95:1. Aquí está el cálculo completo, ejecutado de principio a fin.

La fórmula: dos luminancias y una constante

Todo el contraste de la WCAG 2.x cabe en una línea: relación = (L1 + 0,05) ÷ (L2 + 0,05), donde L1 es la luminancia relativa del más claro de los dos colores y L2 la del más oscuro. Como la especificación los ordena por luminancia y no por papel, la relación nunca depende de a cuál llamaste primer plano. Texto negro sobre blanco y texto blanco sobre negro dan el mismo 21:1.

Todo lo interesante está escondido dentro de L. La luminancia relativa es una magnitud física: estima cuánta luz emite la superficie, normalizada para que el blanco puro valga 1 y el negro puro 0. Deliberadamente no es una medida de lo claro que parece el color al ojo humano, y desde luego no es la L de HSL ni la L de CIELAB, ambas perceptuales y ambas con números muy distintos para el mismo código hexadecimal.

Esa única distinción explica casi todas las discusiones de un diseñador con un comprobador de contraste. La herramienta no juzga el diseño: divide dos números de un modelo colorimétrico normalizado en la época de los tubos de rayos catódicos, que nunca pretendió coincidir con la percepción.

Paso uno: linealizar cada canal

Un valor sRGB de 128 no lleva la mitad de luz que 255. Las codificaciones de pantalla están comprimidas en gamma para gastar el rango limitado de 8 bits allí donde la visión humana es sensible: el número almacenado es, pues, una función no lineal de la luz. Antes de ponderar o sumar nada, cada canal debe devolverse a una magnitud lineal.

La WCAG escribe la función de transferencia de forma explícita. Divide el valor de 8 bits entre 255 para obtener c en [0, 1]. Si c es como mucho 0,03928, el valor lineal es c ÷ 12,92. Si no, es ((c + 0,055) ÷ 1,055) elevado a 2,4. El pequeño tramo lineal cerca del negro existe porque una función potencia pura tiene pendiente infinita en cero, algo numéricamente incómodo y físicamente falso para una pantalla real: sRGB injerta ahí una recta.

Ejecútala y la no linealidad salta a la vista: el canal 128 se linealiza a 0,215861, no a 0,5. El canal 10 da 0,003035 y el canal 11 da 0,003347. Hay aquí una nota que conviene conocer, porque aparece en los informes de error. La WCAG escribe el umbral 0,03928 mientras la norma sRGB de la IEC escribe 0,04045. En 8 bits cortan en 10,0164 y 10,31 respectivamente, así que ambos caen entre los enteros 10 y 11 y ninguno selecciona jamás una rama distinta. Un barrido de los 16 777 216 colores sRGB frente al blanco produjo cero cambios de veredicto y una diferencia máxima de relación exactamente nula. La discrepancia es real en el texto y no afecta a ningún resultado de 8 bits.

Paso dos: los pesos, donde gana el verde

Los tres canales lineales se combinan como L = 0,2126 R + 0,7152 V + 0,0722 A. Esos coeficientes son los pesos de luminancia de la Rec. 709 y suman exactamente 1, que es lo que sitúa el blanco en 1,000000. Codifican un hecho sobre la retina humana, no sobre la pantalla: los conos que dominan la percepción de luminancia son mucho más sensibles al centro del espectro visible que a sus extremos.

Las consecuencias son crudas en cuanto calculas las primarias. El rojo puro #ff0000 tiene luminancia relativa 0,212600. El verde puro #00ff00 tiene 0,715200. El azul puro #0000ff tiene 0,072200. A idéntica intensidad de canal, el verde vale casi diez veces la luminancia del azul. Que un color apruebe una comprobación de contraste depende, pues, mucho más de su contenido de verde que de lo que un diseñador percibe como su oscuridad.

Una comprobación útil: busca el gris con la misma luminancia relativa que el azul puro. Es #4c4c4c, con 0,072272 frente a 0,072200 del azul. Ambos sacan 8,59:1 sobre blanco. Para el modelo esos dos colores son intercambiables a efectos de contraste, cosa que visiblemente no son para un lector — y esa brecha es justo la crítica que le hacen a la WCAG 2 quienes construyen su sucesora.

El azul puro aprueba, el gris medio suspende

Pasa las dos por la fórmula. Azul puro sobre blanco: L1 = 1,000000, L2 = 0,072200, relación = 1,05 ÷ 0,122200 = 8,59:1. Eso supera el AA de texto de cuerpo con 4,5:1 y el AAA con 7:1. Gris medio #808080 sobre blanco: L2 = 0,215861, relación = 1,05 ÷ 0,265861 = 3,95:1, que suspende de plano el AA de texto de cuerpo y apenas supera el listón de 3:1 del texto grande. La mayoría de la gente, si le pides que adivine, ordena esos dos colores al revés.

La misma ponderación explica el verde puro. #00ff00 sobre blanco saca 1,37:1 — peor que casi cualquier color que un diseñador consideraría — porque el verde es el 71,52 % de la luminancia y un verde pleno es, por tanto, casi tan luminoso como el blanco. Sobre negro, ese mismo verde saca 15,30:1. El azul puro sobre negro saca solo 2,44:1 y suspende incluso el umbral no textual de 3:1, justo lo inverso de su excelente resultado sobre blanco.

Para los grises, la frontera merece memorizarse. Barriendo la rampa neutra frente al blanco, #767676 da 4,5422:1 y aprueba el AA, mientras que el valor inmediatamente siguiente #777777 da 4,4781:1 y suspende. #767676 es, pues, el gris más claro capaz de llevar texto de cuerpo sobre fondo blanco, y #595959 cae en 7,00:1, exactamente la línea AAA.

Para qué sirve el 0,05

El negro tiene luminancia relativa 0. Dividir cualquier cosa entre 0 da una relación infinita, algo inútil como puntuación y falso como descripción de una pantalla real. El 0,05 añadido a ambos términos representa la luz ambiente reflejada en la superficie de la pantalla: un píxel negro nunca es realmente negro en una habitación iluminada, y la constante aproxima el velo que lo levanta.

La aritmética es inmediata. El blanco sobre negro pasa a ser (1 + 0,05) ÷ (0 + 0,05) = 1,05 ÷ 0,05 = 21. De ahí sale el famoso techo de 21:1 — no es una decisión de diseño, es 1,05 dividido entre 0,05. Quita la constante y el mismo par se evalúa a infinito, que es lo que devuelve una implementación ingenua y lo que varias bibliotecas de contraste tempranas llegaron a publicar.

La constante también comprime el extremo oscuro de la escala. Dos colores casi negros cuya luminancia difiere en un factor diez tienen relaciones de contraste casi idénticas, porque 0,05 domina ambos denominadores. Es intencionado — a esos niveles la diferencia es realmente invisible con luz normal — pero explica por qué las paletas de modo oscuro parecen más difíciles de ajustar: la fórmula deja de discriminar justo donde viven los temas oscuros.

Los umbrales y qué significa texto grande en cifras

El criterio 1.4.3 (Contraste mínimo, nivel AA) exige 4,5:1 para texto e imágenes de texto, bajando a 3:1 para texto grande. El criterio 1.4.6 (Contraste mejorado, nivel AAA) sube esos valores a 7:1 y 4,5:1. El criterio 1.4.11 (Contraste no textual, nivel AA) exige 3:1 para componentes de interfaz y objetos gráficos necesarios para comprender el contenido — bordes de formulario, indicadores de foco, glifos de iconos, segmentos de gráficos.

El texto grande se define en puntos, no en píxeles: al menos 18 puntos, o al menos 14 puntos si es negrita. Convirtiendo con la referencia CSS de 1 pt = 4/3 px salen 24 px y 18,67 px en negrita. Esos son los números que hay que comprobar en una hoja de estilos. Un titular de 20 px no es texto grande según esta definición y sigue necesitando 4,5:1, lo que pilla a muchísimos diseños que dieron por exentos los titulares.

Dos exenciones merecen conocerse, porque se malinterpretan a menudo. El texto que forma parte de un componente de interfaz inactivo, la decoración pura o lo invisible para todos no tienen requisito de contraste. Y los logotipos — texto que forma parte de un logo o nombre de marca — también están exentos. Ninguna de esas exenciones cubre un texto de sugerencia poco contrastado en un campo de formulario ni una etiqueta atenuada que los usuarios aún deben leer.

Dónde encajan APCA y la WCAG 3

El resultado gris-azul de arriba no es un fallo de implementación: es una limitación del modelo. Una relación de luminancia ignora el tono, la saturación, el grosor de la tipografía, el ancho del trazo y cuál de los dos colores está encima — todos ellos factores que afectan de forma medible a la legibilidad. Una tipografía fina de peso 400 a 4,5:1 puede ser más difícil de leer que una gruesa a 4:1, y la fórmula no puede decirlo.

APCA, el Accessible Perceptual Contrast Algorithm, es la respuesta. Modela la claridad percibida en lugar de la luminancia física, tiene en cuenta deliberadamente la polaridad — oscuro sobre claro y claro sobre oscuro dan puntuaciones distintas para el mismo par — y produce un valor con signo en una escala aproximada de menos 108 a más 106 en vez de una relación, con el objetivo dependiendo conjuntamente del tamaño y el peso de la fuente.

Su estatus hay que enunciarlo con cuidado. La WCAG 3 es un borrador de trabajo del W3C — un documento temprano, explícitamente inestable, del que el propio W3C advierte que no debe servir de base para implementar. APCA figura ahí como método candidato, no como requisito ratificado. Hoy no hay en ninguna parte una evaluación legal o contractual contra APCA. La WCAG 2.2 es la recomendación W3C vigente y es la que referencian de verdad las políticas de accesibilidad, la contratación pública y los litigios. Usa APCA como segunda opinión cuando la relación y tus ojos discrepen; publica contra los números de la 2.x.

L más oscura
Pares reales calculados con la definición WCAG 2.x: luminancias relativas, relación obtenida y veredicto en cada umbral
ParL más claraL más oscuraRelaciónAA cuerpo 4,5:1AA grande 3:1AAA cuerpo 7:1
#000000 sobre #ffffff1,0000000,00000021,00:1ApruebaApruebaAprueba
#0000ff sobre #ffffff1,0000000,0722008,59:1ApruebaApruebaAprueba
#595959 sobre #ffffff1,0000000,0998997,00:1ApruebaApruebaAprueba (justo en la línea)
#767676 sobre #ffffff1,0000000,1811644,54:1Aprueba (el gris más claro que lo logra)ApruebaSuspende
#777777 sobre #ffffff1,0000000,1844754,48:1Suspende (un paso más oscuro aprueba)ApruebaSuspende
#ff0000 sobre #ffffff1,0000000,2126004,00:1SuspendeApruebaSuspende
#808080 sobre #ffffff1,0000000,2158613,95:1SuspendeApruebaSuspende
#00ff00 sobre #ffffff1,0000000,7152001,37:1SuspendeSuspendeSuspende
Comprobador de contraste de colorRelación de contraste WCAG entre texto y fondo, con aprobado/fallo AA/AAA y una vista previa.Probar la herramienta

Preguntas frecuentes

¿Por qué el azul puro sobre blanco aprueba y el gris medio suspende?
Porque la fórmula pondera el verde con 0,7152 y el azul con solo 0,0722. El azul puro no tiene nada de rojo ni de verde, así que su luminancia relativa es 0,072200 — muy oscuro según el modelo, aunque parezca vivo. El gris medio #808080 tiene cantidades iguales de los tres canales y, tras linealizar, su luminancia es 0,215861, tres veces más. Frente al blanco eso da 8,59:1 para el azul y 3,95:1 para el gris: el azul supera AA y AAA mientras el gris suspende el texto de cuerpo AA. Nada ha fallado: la luminancia relativa mide emisión de luz, no lo colorido ni lo oscuro que parece algo. La lección práctica es dejar de adivinar. Los azules, morados y rojos profundos saturados puntúan sistemáticamente mejor de lo que su aspecto sugiere, y los amarillos, cianes y verdes claros mucho peor. Con una paleta de marca muy coloreada, mide cada par en vez de juzgar a ojo: la intuición sobre qué es oscuro está entrenada en la claridad percibida, y la fórmula no.
¿Cambia la relación si intercambio el color de primer plano y el de fondo?
No. La WCAG 2.x define L1 como la luminancia relativa del color más claro y L2 como la del más oscuro, así que el orden lo deciden las propias luminancias, no cuál pintaste con el texto. Texto negro sobre blanco y texto blanco sobre negro dan ambos 21:1. Texto azul #0000ff sobre blanco y texto blanco sobre azul dan ambos 8,59:1. Esa simetría es una propiedad real del modelo 2.x y una limitación real, porque la polaridad sí afecta a la legibilidad: el texto claro sobre fondo oscuro tiende a parecer más fino y puede desbordarse en algunas pantallas, un efecto que la relación no puede representar. APCA, el algoritmo candidato del borrador de la WCAG 3, rompe esa simetría a propósito y devuelve valores distintos según el orden. Bajo la WCAG 2.x, en cambio, un comprobador que dé otra respuesta al intercambiar los campos tiene un error.
¿Cuál es el gris más claro que puedo usar para texto de cuerpo sobre blanco?
#767676. Barriendo la rampa neutra frente a #ffffff, ese valor produce 4,5422:1 y supera el umbral AA de 4,5:1. El siguiente paso, #777777, produce 4,4781:1 y suspende. No hay ningún gris entre ambos — son valores de 8 bits adyacentes — así que #767676 es exactamente la frontera. Dos consecuencias prácticas. Primero, si tu sistema de diseño usa un token como grey-500 para texto secundario, comprueba su hexadecimal real: muchísimos sistemas caen entre 0x77 y 0x99 y no cumplen para texto de cuerpo pareciendo del todo deliberados. Segundo, si necesitas AAA, la frontera equivalente es #595959, que da exactamente 7,00:1. Ambas cifras suponen fondo blanco puro; sobre una superficie blanco roto como #fafafa toda la escala se desplaza y hay que recalcular. Para eso está el comprobador de contraste: la frontera se mueve con el fondo, y los valores memorizados dejan de ser ciertos en cuanto el fondo cambia.
¿Los iconos, bordes de formulario y anillos de foco también necesitan 4,5:1?
No — caen bajo el criterio 1.4.11, Contraste no textual, que pide 3:1 en el nivel AA. Se aplica a dos cosas: la información visual necesaria para identificar un componente de interfaz y su estado, y los objetos gráficos necesarios para comprender el contenido. Así, el borde que indica dónde empieza un campo de texto necesita 3:1 frente a su entorno, igual que un indicador de foco, una casilla marcada, un interruptor en posición activa y los segmentos de distinto color de un gráfico que el lector debe distinguir. Lo que no cubre: la decoración, el estado inactivo de un componente, o los gráficos cuya presentación concreta es esencial — una fotografía, una bandera, una captura de otro producto. Ojo: la comparación es con los colores adyacentes, que para un anillo de foco significa el fondo sobre el que se apoya y para un icono en un botón de color significa el relleno del botón, no la página. Este único criterio detecta más defectos reales en las bibliotecas de componentes modernas que las reglas de texto, porque los campos dibujados con un borde gris claro de 1 px son casi universales.
¿APCA sustituye a la regla de 4,5:1? ¿Debería cambiarme ya?
Todavía no, y quizá no con esa forma. La WCAG 3 es un borrador de trabajo del W3C, estado que significa explícitamente inestable e inadecuado como base de implementación; su propio texto advierte de no citarlo como algo cerrado. APCA aparece allí como método candidato en evaluación, junto a preguntas abiertas sobre los umbrales y sobre cómo se puntuaría siquiera la conformidad. Mientras tanto la WCAG 2.2 es una recomendación del W3C, y es la WCAG 2.x lo que señalan las legislaciones de accesibilidad, las normas de contratación pública y las reclamaciones judiciales en todo el mundo. Hoy no se evalúa nada contra APCA. La postura sensata es seguir cumpliendo la 2.x — 4,5:1, 3:1, 7:1, calculados como arriba — y tratar APCA como diagnóstico cuando la relación y tus ojos discrepen, lo que ocurre sobre todo con tonos saturados y con tipografía fina sobre fondos oscuros. Si APCA dice que un par es peor de lo que la relación sugiere, conviene actuar; si dice que es mejor, sigues sin poder publicar por debajo de 4,5:1 y reclamar AA.
¿Cómo calculo la relación a mano para un color concreto?
Toma #1a73e8 sobre blanco como ejemplo resuelto. Divide el hexadecimal en canales: 26, 115, 232. Divide cada uno entre 255: 0,101961, 0,450980, 0,909804. Los tres superan 0,03928, así que los tres van por la rama de potencia: ((c + 0,055) ÷ 1,055) a la 2,4, dando 0,010330, 0,171441, 0,806952. Aplica los pesos: 0,2126 × 0,010330 = 0,002196, 0,7152 × 0,171441 = 0,122615, 0,0722 × 0,806952 = 0,058262. Súmalos para L = 0,183073. El blanco es 1,000000 y es el más claro, así que la relación es (1 + 0,05) ÷ (0,183073 + 0,05) = 1,05 ÷ 0,233073 = 4,51:1. Eso supera el AA de texto de cuerpo por seis centésimas, cosa que conviene notar: un color tan cerca de la línea suspenderá en cuanto alguien lo aclare un poco, así que trata el rango 4,5 a 4,6 como banda de aviso más que como aprobado. Todo el procedimiento son doce líneas de código en cualquier lenguaje, por eso todos los comprobadores coinciden hasta el segundo decimal — la única fuente real de discrepancia entre herramientas es el redondeo.

Artículos que podrían interesarte

Todas las guías
ExplicaciónDegradados, bandeado y por qué el centro se ve turbioInterpolar en sRGB promedia números codificados en gamma: el punto medio de rojo a verde es #808000 cuando la respuesta a media luz es #bcbc00 — un 57,2 % menos de luz. El bandeado es otro problema aritmético: 8 bits dan 256 escalones, y un degradado oscuro puede tener sólo 28. Aquí están ambos, calculados.ExplicaciónLos esquemas de color son geometría sobre una rueda — y la rueda está malComplementario, 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.TutorialHEX vs RGB: cómo leer y convertir coloresHEX y RGB son dos notaciones para los mismos colores. Aquí tienes cómo leer cada una, convertir entre ellas y añadir transparencia.GuíaQué puede quitar un minificador y qué no debe tocarLa minificación tiene que preservar el significado, y lo interesante son los espacios que llevan significado: el combinador descendiente, los espacios dentro de calc(), el hueco entre dos elementos en línea. Medido aquí sobre archivos reales, incluido lo que brotli te iba a ahorrar de todas formas.ExplicaciónCómo funcionan de verdad las fuentes de Instagram (no son fuentes)Nada se estiliza. Cada letra se sustituye por otro carácter Unicode que resulta parecerse a negrita o cursiva, y por eso un lector de pantalla lee un galimatías y algunos dispositivos muestran cuadros vacíos.ExplicaciónLo que un porcentaje de disponibilidad permite en realidadTres nueves suena a promesa hasta que lo divides en minutos. Lo que compra un 99,9 % al año, al mes, a la semana y al día; por qué la ventana de medición importa mucho más que el nueve de más; y los dos meses distintos que esta herramienta usa para un mismo identificador.

Herramientas relacionadas

Fuentes

¿Has detectado un error en este artículo?