Saltar al contenido principal
Volver a la página del blog

Cuando HTML se queda corto: roles de ARIA

Tiempo de lectura: 6 min
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:

Lenguaje del código:HTML
<div class="tabs">
  <button>Perfil</button>
  <button>Seguridad</button>
  <button>Facturación</button>
</div>
<div>Contenido de la sección seleccionada</div>

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:

Lenguaje del código:HTML
<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>

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.

Lenguaje del código:HTML
<div class="custom-scroll-track">
  <div
    role="scrollbar"
    aria-orientation="vertical"
    aria-valuemin="0"
    aria-valuemax="100"
    aria-valuenow="25"></div>
</div>

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:

Lenguaje del código:HTML
<div class="modal">
  <h1>Confirmar eliminación</h1>
  <p>Esta acción no se puede deshacer.</p>
  <button>Cancelar</button>
  <button>Eliminar</button>
</div>

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:

Lenguaje del código:HTML
<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>

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.

Lenguaje del código:HTML
<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>

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

Lenguaje del código:HTML
<div role="status">Guardado correctamente</div>

O si necesitamos explícitamente interrumpir a la persona ya que es un asunto urgente, tenemos role=”alert”

Lenguaje del código:HTML
<div role="alert">La sesión va a expirar</div>

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".

Lenguaje del código:HTML
<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>

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.

Lenguaje del código:HTML
<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>

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. 

Lenguaje del código:HTML
<div
  role="slider"
  aria-valuemin="0"
  aria-valuemax="100"
  aria-valuenow="40"
  aria-label="Volumen"></div>

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.

Lenguaje del código:HTML
<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>

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:

Lenguaje del código:HTML
<div role="button">Enviar</div>
<span role="link">Inicio</span>

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.

Escrito por:

  • Mía Salazar

    Desarrolladora front-end especializada en accesibilidad web. Divulgadora y creadora de contenido para impulsar experiencias digitales más inclusivas.

    Visita su perfil de LinkedIn

Únete a nuestra comunidad para aprender y estar al día sobre accesibilidad digital.

Recibirás recursos y artículos semanalmente para que puedas aprender, compartir y ser parte de la comunidad de accesibilidad digital.
¿Quieres colaborar en nuestra Newsletter? Escríbenos a hola@weaaare.com