Cómo crear modales accesibles con Bootstrap o Tailwind

Cómo crear modales accesibles con Bootstrap y Tailwind
Solucionex
26
Nov 25

Los modales (o diálogos) son muy comunes en las interfaces web: inicios de sesión, confirmaciones, mensajes de alerta, etc. Pero también son uno de los componentes más problemáticos en accesibilidad web.
Un modal mal implementado puede dejar atrapado al usuario, impedir el uso del teclado o confundir al lector de pantalla.

En este post veremos cómo hacer modales accesibles, primero con Bootstrap y luego con Tailwind.

Problemas comunes en modales

  1. El foco se pierde → Al abrir un modal, el foco debe moverse al interior.

  2. No hay trampa de foco → El usuario puede tabular y “escapar” del modal sin querer.

  3. Contenido detrás accesible → El contenido de fondo no debería ser navegable mientras el modal esté abierto.

  4. Falta de roles ARIA → Sin role="dialog" o aria-modal="true", el lector de pantalla no lo entiende como modal.

  5. Cierre inaccesible → Botón sin aria-label, o solo cierre con ratón.

Modal accesible con Bootstrap 5

Bootstrap ya trae un componente modal que resuelve gran parte de la accesibilidad web:

<!-- Botón para abrir modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
  Abrir modal
</button>

<!-- Modal -->
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title" id="exampleModalLabel">Título del modal</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Cerrar"></button>
      </div>
      <div class="modal-body">
        Contenido del modal aquí...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cerrar</button>
        <button type="button" class="btn btn-primary">Guardar cambios</button>
      </div>
    </div>
  </div>
</div>

👉 Bootstrap gestiona automáticamente:

  • El foco se mueve al modal al abrirlo.

  • El contenido de fondo se bloquea.

  • aria-modal="true" y roles correctos se aplican.

Modal accesible con Tailwind

Con Tailwind debemos programar la lógica manualmente:

<!-- Botón -->
<button id="openModal" class="px-4 py-2 bg-blue-600 text-white rounded">Abrir modal</button>

<!-- Overlay -->
<div id="modal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center"
     role="dialog" aria-modal="true" aria-labelledby="modalTitle">
  <div class="bg-white rounded-lg shadow-lg w-96 p-6 relative">
    <h2 id="modalTitle" class="text-xl font-semibold">Título del modal</h2>
    <p class="mt-2">Este es el contenido del modal accesible.</p>
    
    <div class="mt-4 flex justify-end space-x-2">
      <button id="closeModal" class="px-4 py-2 bg-gray-300 rounded">Cerrar</button>
      <button class="px-4 py-2 bg-blue-600 text-white rounded">Aceptar</button>
    </div>
  </div>
</div>

<script>
  const modal = document.getElementById("modal");
  const openBtn = document.getElementById("openModal");
  const closeBtn = document.getElementById("closeModal");

  openBtn.addEventListener("click", () => {
    modal.classList.remove("hidden");
    // Mover el foco al modal
    modal.querySelector("h2").focus();
  });

  closeBtn.addEventListener("click", () => {
    modal.classList.add("hidden");
    openBtn.focus();
  });

  // Cerrar con Escape
  document.addEventListener("keydown", (e) => {
    if (!modal.classList.contains("hidden") && e.key === "Escape") {
      modal.classList.add("hidden");
      openBtn.focus();
    }
  });
</script>

👉 Claves de esta implementación:

  • role="dialog" + aria-modal="true".

  • Foco automático dentro del modal al abrir.

  • Retorno del foco al botón al cerrar.

  • Soporte de teclado (Esc para cerrar).

  • Fondo inaccesible porque queda fuera de foco.

Buenas prácticas en modales accesibles

  • Siempre usar role="dialog" y **aria-modal="true".

  • Mover el foco automáticamente dentro del modal.

  • Mantener la trampa de foco dentro del modal.

  • Ofrecer un botón de cierre con aria-label="Cerrar".

  • Permitir cierre con Esc además del ratón.

  • Bloquear navegación al contenido de fondo.

Conclusión

Los modales pueden ser uno de los componentes más complicados de hacer accesibles, pero con buenas prácticas y un poco de lógica extra, se puede garantizar que cualquier usuario los use sin problemas.

  • En Bootstrap la accesibilidad web viene casi lista.

  • En Tailwind, debemos implementar la gestión de foco y los atributos ARIA manualmente.

Bootstrap
tailwindcss
Accesibilidad Web
Frontend