¿Qué es ARIA?

ARIA (Accessible Rich Internet Applications) es una especificación del W3C que permite mejorar la accesibilidad de aplicaciones web ricas y componentes dinámicos.

🏷️ Roles

Definen qué es un elemento (button, navigation, alert...)

role="button"

📊 Estados

Describen el estado actual de un elemento (checked, expanded, disabled...)

aria-expanded="true"

🔧 Propiedades

Proporcionan información adicional (label, describedby, controls...)

aria-label="Menú principal"

⚠️ Regla de Oro de ARIA

No uses ARIA si puedes usar HTML nativo

Formularios con atributos ARIA

Mal ejemplo

Formulario sin ARIA

❌ Formulario inaccesible sin atributos ARIA apropiados:

<!-- Malo: sin etiqueta, sin descripción -->
<input type="text" placeholder="Tu correo electrónico">

<!-- Malo: icono no accesible -->
<button>
  <span class="icon">🔍</span>
</button>

<!-- Malo: error no asociado, sin autocompletado -->
<input type="password">
<div class="error">Contraseña demasiado corta</div>
Contraseña demasiado corta
Acepto los términos de uso

Buen ejemplo

Formulario con ARIA

✅ Formulario accesible con atributos ARIA apropiados:

<!-- Bueno: aria-label para campo sin etiqueta visible -->
<input
  type="text"
  aria-label="Dirección de correo electrónico (obligatorio)"
  aria-describedby="email-desc"
  aria-required="true">
<span id="email-desc">Formato: nombre@ejemplo.com</span>

<!-- Bueno: botón con aria-label -->
<button aria-label="Enviar formulario">
  <span aria-hidden="true">🔍</span>
</button>

<!-- Bueno: error asociado con aria-describedby -->
<input
  type="password"
  aria-label="Contraseña (8 caracteres mínimo)"
  aria-invalid="true"
  aria-describedby="pwd-error"
  autocomplete="new-password">
<div id="pwd-error" role="alert">
  La contraseña debe contener al menos 8 caracteres
</div>
Formato: nombre@ejemplo.com
Mínimo 8 caracteres con números y letras
Opciones de notificación
Recibir actualizaciones importantes por correo electrónico
Recibir alertas urgentes por SMS
Atributos ARIA usados:
  • aria-label: Proporciona una etiqueta accesible
  • aria-describedby: Asocia una descripción detallada
  • aria-required: Indica un campo obligatorio
  • aria-invalid: Señala un campo inválido
  • aria-labelledby: Referencia una etiqueta existente
  • role="alert": Anuncia errores inmediatamente
  • aria-hidden: Oculta elementos decorativos
  • aria-live: Anuncia cambios dinámicos

Explicación: Los atributos ARIA enriquecen los formularios proporcionando etiquetas, descripciones y estados a las tecnologías de asistencia.

Acordeón accesible

Mal ejemplo

Sin atributos ARIA

¿Qué es la accesibilidad web?+
¿Por qué es importante?+
¿Cómo empezar?+
<!-- Malo: Sin atributos ARIA, sin navegación con teclado -->
<div class="accordion-header" @click="toggle">
  <span>Title</span>
  <span>+</span>
</div>
<div v-if="open">
  Content
</div>

Buen ejemplo

Con ARIA apropiado

<!-- Bueno: ARIA completo con navegación con teclado -->
<button
  id="accordion-button-1"
  aria-expanded="false"
  aria-controls="panel-1"
  @click="toggle"
  @keydown.enter="toggle"
>
  Title
</button>
<div
  id="panel-1"
  role="region"
  aria-labelledby="accordion-button-1"
  :hidden="!open"
>
  Content
</div>

Explicación: Un acordeón debe usar atributos ARIA apropiados, soportar navegación con teclado y anunciar cambios de estado.

Tabindex y navegación con teclado

El atributo tabindex controla si un elemento puede recibir enfoque del teclado y en qué orden.

✅ tabindex="0"

El elemento es enfocable en el orden natural del DOM

tabindex="0"
<!-- Bueno: Elemento interactivo enfocable -->
<div role="button" aria-label="Custom action" tabindex="0">
  Custom button
</div>

⚠️ tabindex="-1"

Enfocable por JavaScript pero no por Tab

tabindex="-1"
<!-- Bueno: Para enfoque programático -->
<div role="alert" aria-live="assertive" tabindex="-1">
  Error message
</div>

❌ tabindex > 0

Cambia el orden de navegación (¡evitar!)

tabindex="5"
<!-- Malo: Rompe el orden de tabulación natural -->
<button tabindex="3">Third</button>
<button tabindex="1">First</button>
<button tabindex="2">Second</button>

📋 Mejores prácticas de Tabindex

  • Usa tabindex="0" para hacer enfocables elementos interactivos personalizados
  • Usa tabindex="-1" para enfoque programático (modales, alertas)
  • NUNCA uses valores positivos - respeta el orden del DOM
  • Los elementos interactivos nativos (<button>, <a>, <input>) no necesitan tabindex

🎯 Ejemplos interactivos

Prueba la navegación con teclado (Tab/Shift+Tab) con estos elementos:

✅ Elementos enfocables

Enlace
Botón personalizado (div)

❌ Elementos no enfocables

Texto simple (div)
Texto en línea (span)

Párrafo

💡 Consejos de prueba

  • Usa Tab para navegar hacia adelante
  • Usa Shift + Tab para navegar hacia atrás
  • Usa Espacio o Enter para activar elementos
  • Observa el indicador de enfoque (contorno azul)
  • Nota qué elementos pueden o no pueden recibir enfoque

Menú desplegable accesible

Mal ejemplo

Buen ejemplo

Explicación: Un menú desplegable debe soportar navegación con teclado, cerrarse con Escape y mantener el enfoque apropiadamente.

Galería de imágenes accesible

Mal ejemplo

Buen ejemplo

Explicación: Una galería de imágenes debe permitir navegación con teclado y proporcionar descripciones contextuales para cada imagen. El patrón ARIA tablist/tab/tabpanel es perfecto para este caso de uso.

Tabla ordenable accesible

Mal ejemplo

Nombre ↑Correo electrónico Rol Estado
Alice Martinalice@example.comAdminActivo
Bob Dupontbob@example.comUsuarioInactivo
Charlie Leroycharlie@example.comModeradorActivo
Diana Moreaudiana@example.comUsuarioSuspendido

Buen ejemplo

Alice Martinalice@example.comAdminActivo
Bob Dupontbob@example.comUsuarioInactivo
Charlie Leroycharlie@example.comModeradorActivo
Diana Moreaudiana@example.comUsuarioSuspendido
Haz clic en los encabezados de columna para ordenar la tabla. Usa Tab para navegar entre los botones de ordenación.

Explicación: Una tabla ordenable debe indicar el estado de ordenación actual, permitir navegación con teclado y anunciar cambios a los lectores de pantalla.

Regiones dinámicas aria-live

Mal ejemplo

Sin aria-live

Los cambios no se anuncian

<!-- Mauvais : pas d'attribut aria-live -->
<div class="status-banner">
  <p></p>
</div>

Contador: 0

Problemas:

  • Los cambios no se anuncian
  • Los usuarios de lectores de pantalla no saben que el contenido ha cambiado
  • Sin indicación de la importancia del mensaje

Buen ejemplo

Con aria-live apropiado

Los cambios se anuncian según su prioridad

<!-- Bon : avec aria-live="polite" pour informations -->
<div
  class="status-banner"
  aria-live="polite"
  aria-atomic="true"
  role="status"
>
  <p>Information : Données mises à jour</p>
</div>

<!-- Bon : avec aria-live="assertive" pour alertes -->
<div
  class="alert-banner"
  aria-live="assertive"
  aria-atomic="true"
  role="alert"
>
  <p>Attention : Action requise immédiatement</p>
</div>

Zona polite (información)

Ventajas:

  • Los mensajes importantes interrumpen con aria-live="assertive"
  • Los mensajes informativos esperan con aria-live="polite"
  • aria-atomic="true" anuncia el contenido completo de la región
  • Los roles apropiados (status, alert) proporcionan más contexto

Explicación: Las regiones aria-live permiten anunciar cambios de contenido a los lectores de pantalla. 'polite' espera a que termine el anuncio actual, 'assertive' interrumpe inmediatamente.

Botones de iconos accesibles

Mal ejemplo

Botones sin descripción

Estos botones no tienen indicación textual:

Problemas:
  • Los lectores de pantalla solo anuncian "botón"
  • Imposible saber la acción sin ver el icono
  • Los emojis pueden ser malinterpretados
  • Sin tooltip al pasar el ratón

Buen ejemplo

Botones accesibles

Varios enfoques para hacer los iconos accesibles:

1. Con aria-label
2. Con texto visible
3. Con texto visualmente oculto
Mejores prácticas:
  • Proporciona siempre texto alternativo (aria-label, texto oculto o texto visible)
  • Usa aria-hidden="true" en iconos decorativos
  • Agrega tooltips con el atributo title
  • Prefiere texto visible cuando el espacio lo permita

Explicación: Los botones que contienen solo iconos deben tener un aria-label o texto visualmente oculto para ser entendidos por los lectores de pantalla.

Combobox / Búsqueda con sugerencias

Mal ejemplo

Sin ARIA ni navegación con teclado

Buen ejemplo

Con ARIA y navegación con teclado

0 resultado(s) disponible(s)

Explicación: Una búsqueda con sugerencias (combobox) debe ser navegable con teclado, anunciar resultados y permitir la selección.

Notificaciones toast (Live Regions)

Mal ejemplo

Sin aria-live

❌ Las notificaciones no se anuncian a los lectores de pantalla

<!-- Malo: no anunciado a lectores de pantalla -->
<div v-if="showToast" class="toast">
  Votre demande a été enregistrée
</div>

Buen ejemplo

Con aria-live apropiado

✅ Las notificaciones se anuncian correctamente

<!-- Bueno: notificación de información (polite) -->
<div
  v-if="showToast"
  role="status"
  aria-live="polite"
  aria-atomic="true"
  class="toast"
>
  <p>Votre demande a été enregistrée</p>
  <button @click="dismiss" aria-label="Fermer notification">✕</button>
</div>

<!-- Bueno: alerta de error (assertive) -->
<div
  v-if="showError"
  role="alert"
  aria-live="assertive"
  aria-atomic="true"
  class="toast error"
>
  <p>Erreur : action impossible à exécuter</p>
  <button @click="dismiss" aria-label="Fermer alerte">✕</button>
</div>
Diferencias:
  • role="status" + aria-live="polite": Para notificaciones informativas, no interrumpe la lectura actual
  • role="alert" + aria-live="assertive": Para errores críticos, interrumpe inmediatamente
  • aria-atomic="true": Anuncia el contenido completo de la notificación

Explicación: Las notificaciones dinámicas deben ser anunciadas a los lectores de pantalla sin robar el enfoque. Usa role='status' para información y role='alert' para errores.