Si buscas menús animados con CSS3 listos para copiar, aquí tienes cuatro ejemplos completos con su HTML y su CSS. Nada de librerías ni de plugins.
Son menús animados CSS puros: un menú horizontal con subrayado al hover, un desplegable accesible, una hamburguesa responsive solo con CSS y un efecto de relleno deslizante. Todos respetan el foco de teclado y la preferencia de movimiento reducido.
Copia el bloque que te sirva, pégalo en tu proyecto y ajusta los colores. Te explico qué hace cada línea importante para que puedas adaptarlo sin miedo.
Lo que consigues con estos menús animados en CSS3
Un menú de navegación es lo primero que toca tu visitante. Si la animación es suave y el foco se ve, la experiencia mejora sin esfuerzo. Estos son mis criterios para cada ejemplo:
- Cero dependencias. Solo HTML y CSS moderno de 2026.
- Accesible. Uso
:focus-visiblepara que el teclado también vea el estado. - Respetuoso. Con
prefers-reduced-motiondesactivo las transiciones para quien lo pide. - Ligero. Animo
transformyopacity, que no fuerzan repintados costosos.
Regla que sigo siempre: anima
transformyopacityantes quewidth,topomargin. El navegador lo agradece con animaciones más fluidas.
1. Menú horizontal con subrayado animado al hover
El clásico que nunca falla. Una línea que crece bajo el enlace al pasar el ratón. El truco está en un pseudoelemento ::after con transform: scaleX(0) que pasa a scaleX(1). Cambio el transform-origin para que entre por la izquierda y salga por la derecha.
<nav class="menu-subrayado" aria-label="Principal">
<ul>
<li><a href="#">Inicio</a></li>
<li><a href="#">Servicios</a></li>
<li><a href="#">Portfolio</a></li>
<li><a href="#">Contacto</a></li>
</ul>
</nav>
.menu-subrayado ul {
display: flex;
gap: 1.5rem;
list-style: none;
margin: 0;
padding: 0;
}
.menu-subrayado a {
position: relative;
padding: .25rem 0;
color: #1a1a2e;
font-weight: 600;
text-decoration: none;
}
/* La línea que se anima */
.menu-subrayado a::after {
content: "";
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background: #4f46e5;
transform: scaleX(0);
transform-origin: right;
transition: transform .3s ease;
}
.menu-subrayado a:hover::after,
.menu-subrayado a:focus-visible::after {
transform: scaleX(1);
transform-origin: left;
}
/* Foco visible para teclado */
.menu-subrayado a:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 4px;
}
@media (prefers-reduced-motion: reduce) {
.menu-subrayado a::after { transition: none; }
}
Fíjate en la doble regla :hover y :focus-visible. Si solo pones :hover, quien navega con el teclado no ve nada. Con :focus-visible el subrayado aparece también al tabular, y no molesta con el ratón.
2. Menú desplegable (dropdown) accesible con CSS
Un submenú que aparece al pasar por encima y también al enfocar el enlace padre con el teclado. La clave es :focus-within: cuando cualquier elemento del <li> recibe foco, el submenú se muestra. Uso opacity y visibility para animar la entrada.
<nav class="menu-desplegable" aria-label="Principal">
<ul class="nivel-1">
<li><a href="#">Inicio</a></li>
<li class="tiene-submenu">
<a href="#">Recursos</a>
<ul class="submenu">
<li><a href="#">Plantillas</a></li>
<li><a href="#">Iconos</a></li>
<li><a href="#">Tipografías</a></li>
</ul>
</li>
<li><a href="#">Contacto</a></li>
</ul>
</nav>
.menu-desplegable ul {
list-style: none;
margin: 0;
padding: 0;
}
.menu-desplegable .nivel-1 {
display: flex;
gap: 1rem;
}
.menu-desplegable a {
display: block;
padding: .6rem .9rem;
color: #1a1a2e;
text-decoration: none;
border-radius: 6px;
}
.menu-desplegable a:hover,
.menu-desplegable a:focus-visible {
background: #eef2ff;
}
.menu-desplegable a:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 2px;
}
.tiene-submenu { position: relative; }
.submenu {
position: absolute;
top: 100%;
left: 0;
min-width: 180px;
padding: .4rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
opacity: 0;
visibility: hidden;
transform: translateY(6px);
transition: opacity .2s ease, transform .2s ease, visibility .2s;
}
/* Abre con ratón y con teclado */
.tiene-submenu:hover > .submenu,
.tiene-submenu:focus-within > .submenu {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.submenu { transition: none; }
}
Este patrón cubre el caso básico y es válido para muchos sitios. Ahora bien, un desplegable de verdad robusto cierra con la tecla Escape y se recorre con las flechas. Eso ya pide JavaScript, y te lo cuento más abajo.
3. Menú hamburguesa responsive solo con CSS
En móvil necesitas un botón que abra y cierre los enlaces. Se puede hacer sin JavaScript con el «truco del checkbox»: una casilla oculta guarda el estado y el <label> hace de botón. El selector :checked muestra el menú y transforma las barras en una X.
<nav class="menu-hamburguesa" aria-label="Principal">
<a href="#" class="logo">MiSitio</a>
<input type="checkbox" id="abrir-menu" class="toggle-menu">
<label for="abrir-menu" class="icono-hamburguesa">
<span class="sr-only">Abrir menú</span>
<span class="barra" aria-hidden="true"></span>
<span class="barra" aria-hidden="true"></span>
<span class="barra" aria-hidden="true"></span>
</label>
<ul class="enlaces">
<li><a href="#">Inicio</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Sobre mí</a></li>
<li><a href="#">Contacto</a></li>
</ul>
</nav>
.menu-hamburguesa {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
padding: .75rem 1rem;
background: #1a1a2e;
}
.menu-hamburguesa .logo {
color: #fff;
font-weight: 700;
text-decoration: none;
}
.menu-hamburguesa .enlaces {
display: flex;
gap: 1.25rem;
list-style: none;
margin: 0;
padding: 0;
}
.menu-hamburguesa .enlaces a {
color: #e5e7eb;
text-decoration: none;
}
.menu-hamburguesa .enlaces a:hover,
.menu-hamburguesa .enlaces a:focus-visible {
color: #fff;
text-decoration: underline;
}
/* Ocultos en escritorio */
.toggle-menu,
.icono-hamburguesa { display: none; }
/* Texto solo para lectores de pantalla */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
border: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
@media (max-width: 640px) {
.icono-hamburguesa {
display: inline-flex;
flex-direction: column;
gap: 5px;
padding: 6px;
cursor: pointer;
}
.icono-hamburguesa .barra {
width: 26px;
height: 3px;
background: #fff;
border-radius: 2px;
transition: transform .3s ease, opacity .3s ease;
}
.menu-hamburguesa .enlaces {
flex-basis: 100%;
flex-direction: column;
gap: .5rem;
max-height: 0;
overflow: hidden;
transition: max-height .3s ease;
}
.toggle-menu:checked ~ .enlaces {
max-height: 320px;
margin-top: .75rem;
}
/* Barras a forma de X */
.toggle-menu:checked ~ .icono-hamburguesa .barra:nth-child(2) {
transform: rotate(45deg) translate(4px, 4px);
}
.toggle-menu:checked ~ .icono-hamburguesa .barra:nth-child(3) {
opacity: 0;
}
.toggle-menu:checked ~ .icono-hamburguesa .barra:nth-child(4) {
transform: rotate(-45deg) translate(5px, -5px);
}
/* Foco visible sobre el icono */
.toggle-menu:focus-visible ~ .icono-hamburguesa {
outline: 2px solid #fff;
outline-offset: 2px;
}
}
@media (prefers-reduced-motion: reduce) {
.menu-hamburguesa .enlaces,
.icono-hamburguesa .barra { transition: none; }
}
El span.sr-only da un nombre al botón para quien usa lector de pantalla. Las tres barras llevan aria-hidden="true" porque son pura decoración. Animo max-height por comodidad; si notas saltos con listas muy largas, cambia a una animación con grid-template-rows.
4. Efecto moderno: relleno deslizante con transiciones suaves
Un toque más actual: un fondo con degradado que sube desde abajo al pasar por encima. Uso un pseudoelemento ::before con scaleY y un overflow: hidden en el enlace para recortar el relleno dentro del botón redondeado.
<nav class="menu-relleno" aria-label="Secundario">
<ul>
<li><a href="#">Inicio</a></li>
<li><a href="#">Cursos</a></li>
<li><a href="#">Recursos</a></li>
<li><a href="#">Contacto</a></li>
</ul>
</nav>
.menu-relleno ul {
display: flex;
gap: .5rem;
list-style: none;
margin: 0;
padding: 0;
}
.menu-relleno a {
position: relative;
z-index: 0;
display: inline-block;
padding: .6rem 1.1rem;
color: #1a1a2e;
text-decoration: none;
border-radius: 999px;
overflow: hidden;
transition: color .3s ease;
}
.menu-relleno a::before {
content: "";
position: absolute;
inset: 0;
z-index: -1;
background: linear-gradient(135deg, #4f46e5, #7c3aed);
transform: scaleY(0);
transform-origin: bottom;
transition: transform .3s ease;
}
.menu-relleno a:hover,
.menu-relleno a:focus-visible {
color: #fff;
}
.menu-relleno a:hover::before,
.menu-relleno a:focus-visible::before {
transform: scaleY(1);
}
.menu-relleno a:focus-visible {
outline: 2px solid #4f46e5;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
.menu-relleno a,
.menu-relleno a::before { transition: none; }
}
El detalle fino es el transform-origin: bottom: el relleno entra desde la base, como si se llenara un vaso. Cambia a top, left o center y tendrás cuatro variantes gratis con la misma base.
Tip: si necesitas una cabecera completa sin partir de cero, en las plantillas HTML de Envato encuentras navegaciones ya montadas que puedes personalizar con estos mismos efectos.
¿Cuándo necesitas JavaScript?
El CSS te lleva muy lejos, pero tiene límites claros. Pásate a JavaScript cuando necesites gestionar estado y accesibilidad avanzada. Estas son las señales:
- Quieres cerrar el desplegable con
Escapeo al hacer clic fuera. - Necesitas recorrer el submenú con las flechas del teclado (patrón
menude la ARIA Authoring Practices). - La hamburguesa debe anunciar su estado con
aria-expanded="true/false", que el truco del checkbox no comunica bien. - El menú cambia según la sesión del usuario, o carga elementos de forma dinámica.
Mi consejo: empieza con estos menús animados CSS3 y añade una capa fina de JavaScript solo para el estado y los eventos de teclado. La animación déjala en el CSS, que es donde rinde mejor. Si quieres profundizar en las transiciones que dan vida a una interfaz, tienes ejemplos concretos en mi guía de microinteracciones web con código.
Más recursos para tu interfaz
Si te has quedado con ganas de más CSS animado, estos tutoriales del blog encajan justo después de montar tu menú:
- Cómo hacer un slider en HTML5 y CSS3: el siguiente componente que casi todo sitio necesita.
- Crear banners animados con CSS3: las mismas técnicas de
transformaplicadas a promociones. - Aprende a usar CSS Grid: para maquetar la cabecera completa alrededor de tu menú.
Y si quieres entender a fondo por qué desactivo las animaciones con la media query, la referencia oficial está en la documentación de MDN sobre prefers-reduced-motion.
Preguntas frecuentes
¿Se pueden hacer menús animados con CSS3 sin JavaScript?
Sí. El subrayado al hover, el desplegable con :focus-within y la hamburguesa con el truco del checkbox funcionan solo con CSS. JavaScript solo hace falta para estados avanzados como cerrar con Escape o navegar con flechas.
¿Por qué uso :focus-visible y no :focus?
:focus-visible muestra el contorno solo cuando el navegador cree que ayuda, sobre todo al usar el teclado. Así el foco se ve para quien tabula, pero no aparece un borde molesto al hacer clic con el ratón.
¿Qué es prefers-reduced-motion y por qué importa?
Es una media query que detecta si la persona ha pedido menos movimiento en su sistema. Hay usuarios a quienes las animaciones les causan mareo. Con prefers-reduced-motion: reduce desactivo las transiciones y mantengo el menú usable.
¿Estos menús CSS funcionan en móvil?
Sí. El ejemplo de la hamburguesa es responsive por diseño. Los demás son horizontales; en pantallas pequeñas conviene apilarlos o integrarlos dentro del patrón de hamburguesa para que no se salgan del ancho.
¿Por qué animar transform y opacity en vez de otras propiedades?
transform y opacity se pueden componer en la GPU sin recalcular el diseño de la página. Animar width, height o margin obliga al navegador a rehacer cálculos en cada fotograma, y ahí es donde notas los tirones.
Ya tienes cuatro bases sólidas para tu navegación. Copia el que encaje, ajusta los colores a tu marca y prueba siempre con el teclado antes de publicar. Si te han servido, compártelos con quien esté peleándose con su menú.


