Los menús de navegación son uno de los componentes más importantes de cualquier sitio web. Si no están bien implementados, pueden ser una barrera enorme para usuarios de lectores de pantalla o personas que navegan solo con teclado.
En este post vamos a ver cómo hacerlos totalmente accesibles, primero con Bootstrap (que ya nos ayuda bastante) y luego con Tailwind (donde tendremos que armar la lógica).
Principios de accesibilidad web en un menú
Estructura semántica correcta
Usar
<nav>para envolver la navegación principal.Usar listas
<ul>y<li>para los enlaces.
Soporte de teclado
Tab→ moverse entre enlaces.EnteroSpace→ abrir/cerrar submenús.↑ ↓ ← →→ moverse entre elementos del submenú.Esc→ cerrar un submenú abierto.
Roles y atributos ARIA
aria-haspopup="true"yaria-expandeden botones que despliegan submenús.Submenús con
role="menu"yrole="menuitem".Relación con
aria-controlsyaria-labelledby.
Menú accesible con Bootstrap 5
Bootstrap ya resuelve buena parte, pero debemos respetar la semántica:
<nav class="navbar navbar-expand-lg navbar-light bg-light" aria-label="Menú principal">
<div class="container-fluid">
<a class="navbar-brand" href="#">Marca</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Abrir menú">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" href="#" aria-current="page">Inicio</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
data-bs-toggle="dropdown" aria-expanded="false" aria-haspopup="true">
Servicios
</a>
<ul class="dropdown-menu" aria-labelledby="navbarDropdown" role="menu">
<li><a class="dropdown-item" href="#" role="menuitem">Servicio 1</a></li>
<li><a class="dropdown-item" href="#" role="menuitem">Servicio 2</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>👉 Bootstrap gestiona aria-expanded, roles y soporte de teclado de forma automática.
Menú accesible con Tailwind
Con Tailwind hay que programar la lógica de despliegue de submenús:
<nav class="bg-gray-100 p-4" aria-label="Menú principal">
<ul class="flex space-x-4">
<li><a href="#" class="hover:underline">Inicio</a></li>
<li class="relative">
<button id="menu-button" aria-haspopup="true" aria-expanded="false"
aria-controls="submenu"
class="hover:underline focus:outline-none focus:ring">
Servicios
</button>
<ul id="submenu" class="hidden absolute left-0 mt-2 bg-white border shadow rounded"
role="menu" aria-labelledby="menu-button">
<li><a href="#" class="block px-4 py-2 hover:bg-gray-200" role="menuitem">Servicio 1</a></li>
<li><a href="#" class="block px-4 py-2 hover:bg-gray-200" role="menuitem">Servicio 2</a></li>
</ul>
</li>
</ul>
</nav>
<script>
const button = document.getElementById("menu-button");
const submenu = document.getElementById("submenu");
button.addEventListener("click", () => {
const expanded = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", !expanded);
submenu.classList.toggle("hidden", expanded);
});
// Cerrar con Esc
button.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
button.setAttribute("aria-expanded", "false");
submenu.classList.add("hidden");
button.focus();
}
});
</script>👉 Aquí:
aria-haspopupyaria-expandedindican el estado del submenú.El submenú se cierra al pulsar
Esc.Puedes mejorar añadiendo navegación con flechas para mover entre elementos.
Conclusión
Los menús son el componente más crítico en términos de accesibilidad web.
Con Bootstrap gran parte del trabajo está hecho, solo debes mantener una estructura correcta.
Con Tailwind hay que programar la lógica, pero con ARIA y un poco de JS se consigue un menú totalmente inclusivo.