Cuando HTML se queda corto: roles de ARIA
- Autor
- Mía Salazar
- Fecha de publicación
- 05 / 03 / 2026
En accesibilidad web repetimos mucho una idea: primero HTML semántico. Y es correcta. Un <button> siempre será mejor que un <div> con onclick. Un <nav> siempre mejor que un contenedor genérico con enlaces.
Pero hay un punto en el que HTML deja de describir correctamente lo que está ocurriendo. No porque esté mal diseñado, sino porque la web moderna construye patrones de interacción que no existen como elementos nativos. Cuando eso ocurre, los productos de apoyo se enfrentan a un conjunto de cajas sin relación lógica entre ellas.
Ahí es donde ARIA tiene sentido: no como sustituto de HTML, sino como una capa que describe comportamientos, relaciones y estados que el marcado por sí solo no puede expresar.
Importante: ARIA solo informa a la tecnología de asistencia sobre qué es un elemento. ARIA no añade funcionalidad de teclado. Si usas un rol de listbox o tree, debes programar mediante JavaScript la gestión del foco y los eventos de teclado (flechas, Esc, Enter) para que el componente sea realmente usable.
El problema real: la semántica de la interacción
HTML describe bien documentos. Encabezados, párrafos, listas, formularios… todo eso funciona perfectamente con tecnologías de asistencia. El problema aparece cuando dejamos de construir documentos y empezamos a construir interfaces.
Una pestaña que cambia contenido, un selector con autocompletado…. Visualmente es evidente lo que pasa. Pero para un producto de apoyo no lo es.
Tabs: tablist, tab y tabpanel
Las pestañas son probablemente el mejor ejemplo de algo que HTML no puede representar por sí solo. No existe <tabs> en HTML.
Si hacemos esto:
<div class="tabs">
<button>Perfil</button>
<button>Seguridad</button>
<button>Facturación</button>
</div>
<div>Contenido de la sección seleccionada</div>Lenguaje del código:HTML
Para un lector de pantalla son solo tres botones y un bloque de texto sin relación entre sí, jamás entenderemos la relación entre estos botones y cuál de ellos está seleccionado.
Con ARIA podemos describir la relación:
<div role="tablist" aria-label="Configuración de cuenta">
<button role="tab" id="tab-profile" aria-selected="true">
Perfil
</button>
<button
role="tab"
id="tab-security"
aria-selected="false">
Seguridad
</button>
<button role="tab" id="tab-billing" aria-selected="false">
Facturación
</button>
</div>
<section
id="panel-profile"
role="tabpanel"
aria-labelledby="tab-profile">
Datos personales…
</section>Lenguaje del código:HTML
Ahora la tecnología de asistencia entiende que existe un número concreto de pestañas, que una de ellas está seleccionada y que están relacionadas. No es decoración. Es información estructural.
Barras de desplazamiento personalizadas: scrollbar
Si por diseño decides no usar el scroll nativo del navegador, debes informar al sistema de que ese elemento actúa como una barra de desplazamiento.
<div class="custom-scroll-track">
<div
role="scrollbar"
aria-orientation="vertical"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="25"></div>
</div>Lenguaje del código:HTML
Diálogos personalizados: dialog
HTML5 introdujo el elemento <dialog>, pero muchos proyectos siguen utilizando modales completamente personalizados construidos con <div> y JavaScript.
Si hacemos esto:
<div class="modal">
<h1>Confirmar eliminación</h1>
<p>Esta acción no se puede deshacer.</p>
<button>Cancelar</button>
<button>Eliminar</button>
</div>Lenguaje del código:HTML
Para un lector de pantalla esto es simplemente contenido más en el flujo del documento. Con ARIA podemos comunicar que se trata de una ventana de diálogo independiente:
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirmar eliminación</h2>
<p>Esta acción no se puede deshacer.</p>
<button>Cancelar</button>
<button>Eliminar</button>
</div>Lenguaje del código:HTML
Aquí ARIA expresa algo que HTML genérico no puede: que el usuario ha entrado en una región interactiva aislada del resto de la página.
Árboles de datos: tree
El rol tree permite a los usuarios de lectores de pantalla navegar por estructuras jerárquicas (como un explorador de archivos) sabiendo qué nodos están expandidos o colapsados.
<ul role="tree" aria-label="Explorador de archivos">
<li role="treeitem" aria-expanded="true">
<span>Documentos</span>
<ul role="group">
<li role="treeitem">Factura_enero.pdf</li>
<li role="treeitem">CV_final.docx</li>
</ul>
</li>
<li role="treeitem" aria-expanded="false">Imágenes</li>
</ul>Lenguaje del código:HTML
Actualizaciones dinámicas: status y alert
Hay información que aparece sin recargar la página: formularios, validaciones, notificaciones. Visualmente es obvio. Para accesibilidad no lo es.
Usando role="status" avisaremos al producto de apoyo, sin interrumpir a la persona usuaria, de las notificaciones o de la información que aparezca en pantalla
<div role="status">Guardado correctamente</div>Lenguaje del código:HTML
O si necesitamos explícitamente interrumpir a la persona ya que es un asunto urgente, tenemos role=”alert”
<div role="alert">La sesión va a expirar</div>Lenguaje del código:HTML
Estos roles hacen que el lector de pantalla anuncie el cambio automáticamente.
Combobox y Listbox: Selección avanzada
Cuando un <select> nativo se queda corto (por ejemplo, porque necesitas filtrar una lista de cientos de países o usuarios), recurrimos a este patrón. Se divide en dos piezas clave:
- role="combobox": Es el "director de orquesta". Normalmente se aplica al contenedor o al campo de entrada (input). Su función es avisar al usuario de que ese campo no es un simple texto, sino que controla la aparición de una lista de opciones.
- role="listbox": Es el contenedor de los resultados. Mientras que un estándar solo indica una lista de elementos, un listbox indica una lista de opciones seleccionables. Cada elemento hijo debe llevar el role="option".
<div
role="combobox"
aria-expanded="true"
aria-haspopup="listbox">
<input
type="text"
aria-autocomplete="list"
aria-activedescendant="opt-1" />
<ul id="results-list" role="listbox">
<li id="opt-1" role="option" aria-selected="true">
Madrid
</li>
<li id="opt-2" role="option">Málaga</li>
</ul>
</div>Lenguaje del código:HTML
Grid interactivo: grid
Las tablas HTML funcionan muy bien para datos estáticos, pero una tabla editable con navegación por celdas es un patrón distinto.
<div role="grid" aria-label="Inventario">
<div role="row">
<span role="columnheader">Producto</span>
<span role="columnheader">Stock</span>
</div>
<div role="row">
<span role="gridcell">Teclado</span>
<span role="gridcell">23</span>
</div>
</div>Lenguaje del código:HTML
Este rol comunica que el usuario puede navegar celda a celda como en una hoja de cálculo, algo que una tabla semántica tradicional no cubre en interfaces altamente interactivas.
Slider personalizado: slider
Si usas <input type="range">, ya es accesible. El problema aparece cuando el diseño exige un control completamente custom.
<div
role="slider"
aria-valuemin="0"
aria-valuemax="100"
aria-valuenow="40"
aria-label="Volumen"></div>Lenguaje del código:HTML
Ahora el lector de pantalla puede anunciar el valor actual y el usuario puede modificarlo con teclado
Esta situación es más común de lo que nos gustaría, y suele suceder cuando deseamos crear diferentes tipos de elementos de formulario (inputs, selects, textareas) y no podemos utilizar las etiquetas nativas porque deseamos un comportamiento un poco distinto.
Toolbar: agrupación de controles
Cuando tienes varios botones que están relacionados entre sí (como los controles de un editor de texto o los filtros de una tabla), HTML no expresa esa relación. Para un lector de pantalla, son solo botones sueltos en el flujo de la página.
El rol toolbar agrupa estos elementos en un solo contenedor lógico. Esto permite que el usuario entienda que no son botones aislados, sino herramientas de un mismo grupo funcional.
<div role="toolbar" aria-label="Formato de texto">
<button type="button" aria-pressed="false">
Negrita
</button>
<button type="button" aria-pressed="false">
Cursiva
</button>
<button type="button" aria-pressed="false">
Subrayado
</button>
</div>Lenguaje del código:HTML
El usuario entiende que no son botones aislados, sino herramientas de un mismo grupo.
Cuándo no usar ARIA
Hay una regla sencilla:
Si existe un elemento HTML nativo que hace lo mismo, úsalo.
Malos ejemplos:
<div role="button">Enviar</div>
<span role="link">Inicio</span>Lenguaje del código:HTML
Un <button> y un <a> ya resuelven el problema mejor que cualquier ARIA.
ARIA no arregla una mala elección de HTML. Solo describe comportamientos que HTML no puede describir.
Conclusión
ARIA no es para "hacer accesible" una página. Es para hacer comprensible una interfaz interactiva cuando HTML deja de ser suficiente.
Los roles como tab, switch, combobox, slider o status existen porque la web ya no es solo un documento: es una aplicación. Y cuando construimos aplicaciones, necesitamos comunicar estados, relaciones y expectativas de interacción a quienes no ven la pantalla.
La accesibilidad no consiste en añadir atributos, sino en transmitir significado. Y ARIA, bien usado, es exactamente eso: significado donde antes solo había comportamiento.