wandres.dev
SVG Y ACCESIBILIDAD · Gráficos que se pueden leer

El rol img y cuándo aplicarlo

Qué le hace role img al árbol de accesibilidad, por qué colapsa el subárbol, los roles del módulo de gráficos de ARIA, y los tres casos donde no debes ponerlo.

⏱ 15 min

role="img" sobre un SVG hace dos cosas a la vez: le da un rol que los lectores de pantalla reconocen, y convierte todo su contenido en opaco. La segunda es la importante y la que casi nadie sabe explicar, porque es lo que impide que un lector de pantalla recorra doscientas formas anunciando «gráfico, gráfico, gráfico». También es lo que hace que en tres casos concretos sea la decisión equivocada.

🎯 Al terminar esta lección sabrás
  • Describir qué le hace role="img" al árbol de accesibilidad de un SVG.
  • Explicar por qué el subárbol se vuelve opaco y qué se gana con ello.
  • Enumerar los roles del módulo de gráficos de ARIA y su estado real.
  • Identificar los tres casos donde role="img" es incorrecto.

Qué le hace al árbol

El árbol de accesibilidad es la estructura que el navegador construye a partir del DOM y que las tecnologías de asistencia recorren. Sin role="img", un svg en línea aporta al árbol un nodo con el rol graphics-document, y sus descendientes también aparecen: cada path, cada g, cada text es un nodo.

Eso produce dos problemas.

Ruido. Un gráfico con quinientas formas genera quinientos nodos que el usuario tiene que atravesar. Con la navegación por elementos de un lector de pantalla, recorrer un gráfico se convierte en un castigo.

Anuncios sin sentido. Los elementos text de un SVG sí tienen contenido, así que se anuncian. Un gráfico de barras con etiquetas de eje anuncia los números sueltos, sin contexto ni orden. «Cero. Veinte. Cuarenta. Sesenta. Enero. Febrero.» No es una alternativa textual, es un desguace.

role="img" resuelve las dos: declara que el elemento es una imagen, y por las reglas de cálculo del árbol de accesibilidad, los descendientes de un elemento con rol img no se exponen. El nodo es una hoja. Lo único que el usuario percibe es el rol y el nombre accesible.

De ahí sale la regla más importante de este nivel: si pones role="img", tienes que poner un nombre accesible. Un SVG con role="img" y sin nombre es un agujero: se anuncia como «imagen» sin más, y todo lo que había dentro ha desaparecido. Es estrictamente peor que no poner nada.

<!-- Correcto -->
<svg role="img" aria-labelledby="t" viewBox="0 0 24 24">
  <title id="t">Alerta de temperatura alta</title>
  <path d="..." />
</svg>

<!-- Peor que no hacer nada -->
<svg role="img" viewBox="0 0 24 24">
  <path d="..." />
</svg>

El módulo de gráficos de ARIA

Existe una especificación, el módulo de gráficos de WAI-ARIA, que define tres roles pensados para esto:

Rol Para qué
graphics-document Un gráfico completo con estructura navegable
graphics-object Una parte con significado dentro del gráfico
graphics-symbol Un símbolo que representa un concepto, sin estructura interna

La idea es buena: un gráfico sería un graphics-document, cada serie un graphics-object, y cada marcador un graphics-symbol. El usuario podría navegar la estructura en lugar de recibir un bloque de texto.

El problema es el soporte. Los lectores de pantalla implementan estos roles de forma parcial e inconsistente, y en muchas combinaciones se comportan como roles genéricos. Construir la accesibilidad de un gráfico sobre ellos es construir sobre algo que en la mayoría de los casos no se anuncia.

Recomendación práctica: usa role="img" con un buen nombre y una alternativa textual completa, y considera los roles de gráficos como una mejora progresiva encima, no como la base. Cuando el soporte madure, el gráfico ya funcionaba.

Los tres casos donde no debes ponerlo

Uno: cuando el SVG es interactivo. Si dentro hay elementos que se pueden enfocar, pulsar o navegar (barras que responden al teclado, una leyenda que filtra), role="img" los borra del árbol de accesibilidad y quedan inalcanzables. Un elemento con rol img no puede tener descendientes interactivos expuestos.

En ese caso el SVG no lleva role="img", y cada elemento interactivo lleva su propio rol (button, checkbox), su nombre y su gestión de foco.

Dos: cuando el SVG contiene texto que debe ser accesible por sí mismo. Un diagrama con etiquetas que forman parte del contenido, no de la decoración. Aquí role="img" con un nombre que resuma sería una pérdida de información. La alternativa es dejar el SVG sin rol y estructurar el contenido con text bien organizado, o mejor, poner el texto en HTML.

Tres: cuando el SVG es decorativo. Entonces no lleva role="img": lleva aria-hidden="true", que es otra cosa. Poner role="img" a un adorno lo convierte en un elemento anunciable que no aporta nada.

role img sobre un SVG en línea no siempre basta, y el arreglo es un envoltorio

Hay una asimetría que da problemas y no está documentada en ningún sitio evidente: el cálculo del nombre accesible de un elemento SVG con role="img" funciona bien en los navegadores actuales, pero hay combinaciones de lector de pantalla y modo de navegación donde el SVG en línea sigue sin anunciarse correctamente, sobre todo en el modo de exploración de documento.

El patrón defensivo que usan las bibliotecas que se toman esto en serio consiste en envolver el SVG en un elemento HTML que lleve el rol y el nombre, y ocultar el SVG:

<span role="img" aria-label="Tendencia al alza del 12 por ciento">
  <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
    <path d="..." />
  </svg>
</span>

El span es un elemento HTML corriente, y el cálculo del nombre accesible de un elemento HTML con role="img" y aria-label está soportado universalmente desde hace años. El SVG queda completamente fuera del árbol.

Cuesta un nodo más y elimina toda una clase de fallos. Merece la pena en una biblioteca de componentes que va a usarse en contextos que no controlas.

Y el detalle del focusable="false": fue necesario porque las versiones antiguas de Internet Explorer y del Edge original metían los elementos SVG en el orden de tabulación, produciendo paradas del teclado sobre adornos. Los motores actuales no lo hacen. El atributo sigue apareciendo en todas las bibliotecas por inercia; no hace daño y hoy no hace falta. Si tu matriz de soporte es moderna, puedes quitarlo, y si mantienes código que lo tiene, no lo toques: no es el sitio donde ganar bytes.

Un resumen operativo

Tres preguntas en orden, y la respuesta sale sola.

¿El SVG aporta información que no está en otro sitio? Si no, es decorativo: aria-hidden="true" y punto.

¿Es interactivo por dentro? Si sí, no lleva role="img"; cada parte interactiva lleva su propia semántica.

Si aporta información y no es interactivo: role="img" más un nombre accesible que transmita esa información. Y si la información es densa (un gráfico de datos), además una alternativa textual completa fuera del SVG.

⚔️ Reto práctico

Coge un gráfico de barras de tu proyecto y explóralo con un lector de pantalla en modo de navegación por elementos, primero sin role="img" y luego con él. Cuenta cuántas paradas hay en cada caso y anota qué se anuncia en cada una. La diferencia entre los dos recorridos es el argumento completo de este artículo.