Desarrollo web

Contraste de color WCAG: cómo se calcula, qué ratios exige y cómo cumplirlos

Qué es el ratio de contraste, cómo se calcula a partir de la luminancia, qué exigen los niveles AA y AAA, cómo medirlo y los errores de diseño que más lo incumplen.

El contraste de color es la diferencia de luminosidad entre un texto (o un elemento gráfico) y su fondo, expresada como un ratio de 1:1 (ningún contraste) a 21:1 (negro sobre blanco). Las WCAG, el estándar de accesibilidad web, exigen al menos 4,5:1 para texto normal y 3:1 para texto grande y componentes de interfaz en el nivel AA, y 7:1 en el nivel AAA. No es una preferencia estética: un gris claro sobre blanco que a ti te parece elegante es ilegible para una parte real de los visitantes y lo señalan Lighthouse, axe y cualquier auditoría.

Este artículo explica cómo se calcula el ratio, qué exige cada criterio, cómo medirlo y cómo corregir los casos típicos sin rehacer la paleta.

Cómo se calcula el ratio

La fórmula está en la definición de las WCAG:

ratio = (L1 + 0,05) / (L2 + 0,05)

donde L1 es la luminancia relativa del color más claro y L2 la del más oscuro, ambas entre 0 (negro) y 1 (blanco). La luminancia se obtiene de los componentes sRGB: cada canal se normaliza a 0–1, se linealiza (si es ≤ 0,04045 se divide entre 12,92; si no, ((c + 0,055) / 1,055) ^ 2,4) y se combinan con los pesos 0,2126 R + 0,7152 G + 0,0722 B. El verde pesa mucho más que el azul porque el ojo humano es más sensible a él: por eso un azul saturado sobre negro contrasta menos de lo que parece.

En JavaScript:

function luminance([r, g, b]) {
  const lin = (c) => {
    c /= 255;
    return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
  };
  return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}
function contrast(a, b) {
  const [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
  return (l1 + 0.05) / (l2 + 0.05);
}
contrast([29, 29, 31], [255, 255, 255]); // #1d1d1f sobre blanco ≈ 16,8:1
contrast([110, 110, 115], [255, 255, 255]); // #6e6e73 sobre blanco ≈ 5,1:1
contrast([134, 134, 139], [255, 255, 255]); // #86868b sobre blanco ≈ 3,6:1 (no vale para texto normal)

Los tres ejemplos son los grises de este blog: el primero es el texto principal, el segundo el gris que usamos para texto secundario (pasa AA) y el tercero el gris de Apple que usamos al principio y hubo que descartar para texto pequeño.

Qué exige cada criterio

Criterio Nivel Aplica a Ratio mínimo
1.4.3 Contraste (mínimo) AA Texto normal 4,5:1
1.4.3 Contraste (mínimo) AA Texto grande (≥ 24 px, o ≥ 18,66 px en negrita) 3:1
1.4.6 Contraste (mejorado) AAA Texto normal 7:1
1.4.6 Contraste (mejorado) AAA Texto grande 4,5:1
1.4.11 Contraste no textual AA Componentes de interfaz (bordes de campos, iconos necesarios, indicadores de foco) y gráficos informativos 3:1

Quedan exentos: el texto decorativo, los logotipos, el texto de controles desactivados y el texto que forma parte de una imagen puramente decorativa. Un placeholder de un campo de formulario no está exento: es texto que el usuario necesita leer.

El tamaño «grande» se define en puntos (18 pt, o 14 pt en negrita); en CSS equivale a 24 px y 18,66 px aproximadamente.

Cómo medirlo

  • Herramienta de colores de AIMRAN Tools: introduce el color de texto y el de fondo (en HEX, RGB, HSL u OKLCH) y muestra el ratio y si pasa AA y AAA para texto normal y grande. Funciona en el navegador.
  • Chrome DevTools: al inspeccionar un texto, el selector de color del panel Styles muestra el ratio y dos líneas en la rueda de color que marcan dónde empieza AA y AAA. El panel CSS Overview lista todos los pares con contraste insuficiente de la página.
  • Lighthouse / axe: la auditoría color-contrast enumera los elementos que fallan con su ratio actual.
  • Firefox: el inspector de accesibilidad tiene una comprobación de contraste con simulación de daltonismo.

Las herramientas automáticas no detectan texto sobre imágenes o degradados: ahí hay que medir contra la zona más clara (o más oscura) del fondo que pueda quedar debajo del texto.

Los fallos más frecuentes y cómo corregirlos

  1. Gris «sutil» para texto secundario. #999 sobre blanco da 2,9:1. Baja a #6e6e73 (5,1:1) o #595959 (7:1). La jerarquía se consigue también con tamaño y peso, no solo con color.
  2. Texto blanco sobre colores de marca claros. Blanco sobre #0071e3 da 4,7:1 (pasa); sobre #34c759 (verde) solo 2,2:1. Para etiquetas de colores claros, usa texto oscuro o mezcla el color con negro: en este blog las etiquetas de categoría usan color-mix(in srgb, var(--c) 62%, black) para el texto y el color puro solo en iconos y fondos.
  3. Enlaces que solo se distinguen por el color. Además del contraste con el fondo, un enlace dentro de un párrafo debe diferenciarse del texto normal (las WCAG piden 3:1 entre ambos o una señal adicional, como el subrayado). Subrayar es lo más sencillo.
  4. Placeholders claros. El gris por defecto de muchos navegadores no llega a 4,5:1. Define ::placeholder { color: #6e6e73 }.
  5. Indicador de foco invisible. El outline del teclado debe tener 3:1 contra el fondo adyacente. Un outline: 2px solid del color primario sobre fondo blanco suele cumplir; sobre un botón del mismo color, no: añade un outline-offset para que el contorno quede sobre el fondo.
  6. Modo oscuro sin revisar. Los grises que funcionan sobre blanco no funcionan sobre #000. Mide las dos paletas por separado.

Un apunte sobre APCA

Las WCAG 3, aún en borrador, estudian un algoritmo distinto (APCA) que tiene en cuenta mejor la polaridad (texto claro sobre oscuro frente a oscuro sobre claro) y el tamaño de la fuente. Es interesante como referencia de diseño, pero el requisito legal y el que miden las auditorías hoy es el ratio de las WCAG 2, así que es el que debes cumplir.

Conclusión

El contraste se calcula con una fórmula fija a partir de la luminancia de los dos colores y se compara con tres umbrales: 4,5:1 para texto, 3:1 para texto grande y componentes, 7:1 si aspiras a AAA. Mide con DevTools o una herramienta de contraste, revisa claro y oscuro por separado y recuerda que la jerarquía visual no depende solo del gris: tamaño, peso y espacio hacen el mismo trabajo sin dejar fuera a nadie.

Fuentes y referencias

  1. WCAG 2.2: Criterio 1.4.3 Contraste (mínimo) w3.org
  2. WCAG 2.2: Criterio 1.4.6 Contraste (mejorado) w3.org
  3. WCAG 2.2: Criterio 1.4.11 Contraste no textual w3.org
  4. WCAG 2.2: definición de relación de contraste y luminancia relativa w3.org
  5. web.dev: Background and foreground colors do not have a sufficient contrast ratio developer.chrome.com

Herramientas relacionadas

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas
Artículos de Desarrollo web