Skip to content

Documentación del test step CheckHoverContrast ​

Visión general ​

El test step CheckHoverContrast verifica que los elementos interactivos cumplan los requisitos de WCAG 2.1 SC 1.4.11 (Contraste no textual) para los cambios de estado en hover. En concreto, comprueba que cuando el cambio de color es la única pista visual que indica el estado hover, la relación de contraste entre el estado por defecto y el estado hover sea de al menos 3:1.


Propósito ​

Los controles interactivos suelen cambiar de aspecto al pasar el cursor para indicar que son interactivos. Cuando ese cambio depende únicamente del color (sin otras pistas visuales como un cambio de cursor, una sombra o un borde), las personas con baja visión pueden no percibir el cambio de estado. Este test garantiza que haya contraste suficiente entre ambos estados.


Referencia WCAG ​

  • Criterio de conformidad: 1.4.11 Contraste no textual (Nivel AA)
  • Requisito: la información visual necesaria para identificar componentes de interfaz y sus estados debe tener una relación de contraste de al menos 3:1 frente a los colores adyacentes.
  • Se aplica a: cambios de estado en hover donde el color es el único indicador visual.

Referencias ​


Cómo funciona ​

1. Selección de elementos ​

El test se aplica a los elementos de visually_graphic_elements (iconos, botones con gráficos), ya que son los que con más probabilidad tienen estados hover basados solo en color y sin cambio de cursor.

2. Filtros previos (condiciones de descarte) ​

Antes de evaluar, se descartan los elementos que:

  • No son visibles.
  • Están deshabilitados (atributo disabled o aria-disabled="true").

3. Detección de otras pistas visuales ​

El test solo se aplica cuando el cambio de color es el único indicador visual. Se descartan los elementos que presentan otras pistas:

Pista visualCómo se detecta
Cursor interactivopointer, hand, grab, etc. en el estado por defecto o en hover
Cambio de cursorCualquier cambio de cursor a un valor con significado
Aparición de sombraSe añade box-shadow o text-shadow en hover
Cambios de bordeCambia el grosor, el estilo o la visibilidad del borde
Cambios de transformCambia la propiedad transform (escala, rotación, etc.)
Cambios de tamañoLas dimensiones del elemento cambian más de 2 px
Transforms en SVGTransform en elementos SVG hijos
Cambios de outlineCambia el grosor o el estilo del outline
Decoración de textoCambios de subrayado o tachado
Cambios de opacidadDiferencia de opacidad mayor de 0,1
Estilos por defecto del navegadorEstilado de hover propio del navegador (por ejemplo, el subrayado de los enlaces)

4. Prioridad de extracción de color ​

Para elementos gráficos (iconos), los colores se extraen en este orden de prioridad:

  1. Colores de los SVG hijos — fill y stroke de <svg>, <path>, <circle>, etc.
  2. Propiedades gráficas — fill, stroke, color del propio elemento.
  3. Propiedades del contenedor — background-color y colores de borde.

Así se mide el cambio de color del control, no el del fondo.

5. Comparación basada en capturas ​

El test usa una comparación de capturas de pantalla:

  1. Desplazar el elemento hasta que sea visible.
  2. Capturar el estado por defecto.
  3. Poner el cursor sobre el elemento (espera de 300 ms para que se apliquen los estilos).
  4. Capturar el estado hover.
  5. Comparar las capturas para detectar el cambio visual.
  6. Calcular la relación de contraste mediante análisis de imagen, apoyado en las pistas de color obtenidas del CSS.

6. Cálculo del contraste ​

La relación de contraste se calcula entre:

  • Color del estado por defecto: el color del elemento antes del hover.
  • Color del estado hover: el color del elemento después del hover.

La comparación usa get_hover_contrast_ratio(), que:

  • Analiza las diferencias de píxeles entre capturas.
  • Usa las pistas de color del CSS cuando están disponibles.
  • Recurre al análisis del color dominante cuando no las hay.

Formato del reporte ​

Cuando se encuentra una violación:

CampoValor
Title«Insufficient contrast against the control and background»
DescriptionEl <nombre de color> (#hex) del control «<nombre del control>» en hover no alcanza el contraste mínimo frente al fondo <nombre de color> (#hex), con una relación de <relación de contraste>.
Failed WCAG CP1.4.11

Configuración ​

python
CheckHoverContrast(
    debug=False,           # Activa el log de depuración
    target_key="visually_graphic_elements"  # Clave en context.data con los elementos a revisar
)

Constantes ​

  • WCAG_NON_TEXT_CONTRAST_THRESHOLD: 3.0 (relación de contraste mínima exigida).
  • INTERACTIVE_CURSOR_VALUES: conjunto de valores de cursor que se consideran pista visual (pointer, hand, grab, etc.).

Limitaciones ​

1. Imágenes de fondo ​

  • Problema: si un elemento usa background-image en lugar de background-color, la extracción de color vía CSS no puede obtener el color.
  • Mitigación: la comparación por capturas sigue funcionando analizando los píxeles renderizados, pero la precisión puede bajar.
  • Caso límite: si el hover cambia la propia imagen de fondo (y no solo superpone un color), el cálculo de contraste puede no ser exacto.

2. Degradados complejos ​

  • Problema: los elementos con fondo en degradado (linear-gradient, radial-gradient) no tienen un único valor de color.
  • Mitigación: el análisis de la captura intenta encontrar los colores dominantes, pero los resultados pueden variar.

3. Pseudoelementos ​

  • Problema: los efectos de hover aplicados mediante ::before o ::after pueden no ser detectados por get_all_styles().
  • Mitigación: la comparación de capturas sí detecta el cambio visual, aunque falten las pistas de color del CSS.

4. Animaciones y transiciones ​

  • Problema: en elementos con transiciones CSS, la transición puede no haber terminado cuando se toma la captura del hover.
  • Mitigación: se espera 300 ms tras el hover, pero las transiciones muy lentas pueden no completarse.

5. Efectos de hover por JavaScript ​

  • Problema: los efectos disparados por eventos mouseenter de JavaScript pueden tener problemas de sincronización.
  • Mitigación: la espera de 300 ms ayuda, pero las interacciones complejas pueden no capturarse por completo.

6. Elementos interactivos anidados ​

  • Problema: los botones o enlaces anidados pueden tener efectos de hover que interfieren entre sí.
  • Comportamiento actual: cada elemento se evalúa de forma individual.

7. Contenido dinámico ​

  • Problema: los elementos que cargan contenido al hacer hover (tooltips, desplegables) pueden alterar las capturas.
  • Mitigación: normalmente presentan otras pistas visuales, por lo que se descartan.

8. Iconos con sprites SVG ​

  • Problema: los iconos que usan sprites SVG (<use xlink:href>) pueden tener los colores definidos en el sprite y no en el elemento.
  • Mitigación: _get_svg_child_colors() intenta extraer los colores de los elementos hijos.

9. Fuentes de iconos ​

  • Problema: las fuentes de iconos (FontAwesome, etc.) usan la propiedad color, que sí se comprueba, pero puede heredarse de un elemento padre.
  • Mitigación: la propiedad CSS color está incluida entre las propiedades de color gráficas.

10. Modo de alto contraste ​

  • Problema: quienes usan el modo de alto contraste de Windows pueden ver colores distintos de los evaluados.
  • Comportamiento actual: el test se ejecuta con el renderizado estándar del navegador.

Fuera de alcance ​

Este test step no evalúa:

  • Componentes deshabilitados: se descartan automáticamente.
  • Elementos con otras pistas visuales: cambios de cursor, sombras, bordes, etc.
  • Contraste de texto: corresponde a WCAG 1.4.3, no a 1.4.11.
  • Contraste del estado de foco: lo cubre CheckFocusIndicatorContrast.
  • Estados active y visited: solo se evalúa el estado hover.
  • Colores de hover por defecto del navegador: se descartan por ser responsabilidad del navegador.

Dependencias ​

Módulos internos ​

  • BaseTestStep, TestStepResult — infraestructura base de tests.
  • Element — envoltorio del elemento de página, con capacidades de hover y captura.
  • BaseLogger — infraestructura de logging.

Funciones de utilidad ​

  • src.utils.color — utilidades de parseo y conversión de color.
  • src.vision.comparison — comparación de imágenes y cálculo de contraste.
    • compare_byte_images() — detecta diferencias visuales.
    • get_hover_contrast_ratio() — calcula el contraste entre estados.

Ejemplo de uso ​

python
from src.a11y_test_definitions.test_step.visually_disabled.CheckHoverContrast import CheckHoverContrast

# Crear el test step
hover_contrast_check = CheckHoverContrast(debug=True)

# Ejecutarlo como parte de un test case
result = hover_contrast_check.run(context, logger)

Modo depuración ​

Con debug=True, el test muestra información detallada paso a paso:

[CheckHoverContrast]     Step 1: Checking visibility...
[CheckHoverContrast]     Step 2: Checking disabled state...
[CheckHoverContrast]     Step 3: Getting default cursor/styles...
[CheckHoverContrast]     -> Default cursor: auto
[CheckHoverContrast]     -> Default SVG colors: {'fill': 'rgb(102, 102, 102)', 'stroke': ''}
[CheckHoverContrast]     Step 4: Scrolling into view and taking default screenshot...
[CheckHoverContrast]     Step 5: Hovering element...
[CheckHoverContrast]     Step 6: Getting hover cursor...
[CheckHoverContrast]     -> Hover cursor: auto
[CheckHoverContrast]     Step 7: Getting hover styles...
[CheckHoverContrast]     -> Hover SVG colors: {'fill': 'rgb(0, 102, 204)', 'stroke': ''}
[CheckHoverContrast]     Step 10: Comparing screenshots...
[CheckHoverContrast]     Step 11: Extracting color hints...
[CheckHoverContrast]     -> CSS colors: default=(102, 102, 102), hover=(0, 102, 204)
[CheckHoverContrast]     Step 12: Calculating contrast ratio...
[CheckHoverContrast]     -> Contrast: 2.45:1 (fg=(0, 102, 204), bg=(102, 102, 102))

Historial de versiones ​

VersiónCambios
1.0Implementación inicial con extracción de color vía CSS
1.1Extracción de color de SVG hijos para dar soporte a iconos
1.2Comparación basada en capturas para mayor robustez
1.3Detección completa de pistas visuales (cursor, sombra, transform, etc.)