Crear un menú desplegable interactivo no siempre requiere JavaScript. Con dos pseudoclases de CSS, :hover y :focus-within, puedes lograr un navbar funcional, accesible y animado usando solo hojas de estilo. Esta técnica es ideal si buscas mantener tu código ligero y aprovechar al máximo las capacidades nativas del navegador.
¿Cómo se estructura un menú desplegable solo con CSS?
La base es un contenedor con la clase menu que envuelve un botón y una lista de opciones. El truco está en ocultar la lista por defecto y revelarla cuando ocurre una interacción específica.
En el ejemplo, el contenedor menu usa position: relative y display: inline-block [0:38]. Esto permite que toda la zona de navegación funcione como una unidad, no solo el botón. Adentro vive menu-button, con fondo azul, letra blanca, padding de 10 píxeles y un cursor que cambia al pasar por encima [1:10].
La clase clave es menu-list, que arranca oculta gracias a tres propiedades específicas:
position: absolute con top: 40px y left: 0 para posicionarla bajo el botón.
opacity: 0 para que sea completamente transparente al inicio.
transform: scaleY(0) para comprimirla verticalmente y que no ocupe espacio visual.
A esto se suma una transition de tres milisegundos que suaviza la aparición de la opacidad y el transform [2:15].
¿Qué hacen :hover y :focus-within en este menú?
Ambas pseudoclases detectan interacciones del usuario, pero responden a situaciones distintas y se complementan muy bien.
¿Qué es la pseudoclase :hover? Detecta cuando el cursor se posiciona encima de un elemento. En este caso, activa la lista cuando pasas el mouse sobre el contenedor del menú.
¿Qué es :focus-within? Aplica estilos a un elemento padre cuando alguno de sus hijos recibe foco, por ejemplo al hacer clic en el botón. Mantiene el menú abierto incluso si retiras el cursor.
La magia está en aplicar la pseudoclase al contenedor menu, pero modificar la lista hija menu-list. Así, el evento se dispara en el padre y el cambio visual ocurre en el hijo.
¿Cómo se escribe la regla CSS combinada?
La sintaxis usa una coma para encadenar dos escenarios que disparan el mismo cambio:
css
.menu:hover .menu-list,
.menu:focus-within .menu-list {
opacity: 1;
transform: scaleY(1);
}
Cuando se cumple cualquiera de las dos condiciones, la lista pasa a opacity: 1 y scaleY(1), revelándose con la transición definida [3:30]. Es un patrón limpio que cubre tanto la interacción con mouse como la navegación por teclado o clic.
¿Por qué combinar hover y focus-within mejora la experiencia?
Usar solo una de las dos pseudoclases deja huecos en la usabilidad. Al combinarlas, cubres más formas de interacción.
Si únicamente aplicas :hover, el menú aparece al pasar el cursor pero desaparece apenas te alejas. Eso obliga al usuario a mantenerse exactamente sobre el área, lo cual es incómodo en menús con varias opciones [4:40].
Si solo aplicas :focus-within, el hover deja de funcionar y necesitas hacer clic obligatoriamente para abrir la lista. Funciona, pero pierdes la fluidez de la previsualización al pasar el cursor [5:10].
La combinación de ambas resuelve estos detalles:
- Pasas el mouse y el menú aparece de inmediato.
- Si haces clic, el menú permanece abierto gracias al foco activo.
- Solo se cierra cuando haces clic fuera del contenedor o retiras el cursor sin haber hecho clic.
¿Cómo cierro un menú abierto con focus-within? Haz clic fuera del contenedor. Mientras el foco esté dentro del menu, la lista se mantiene visible.
¿Qué ventajas tiene resolver esto sin JavaScript?
Apoyarte en CSS puro reduce el peso de la página, evita dependencias externas y mejora el rendimiento. Además, las pseudoclases nativas funcionan bien con tecnologías de accesibilidad y navegación por teclado.
La propiedad transform: scaleY(0) es especialmente útil porque, a diferencia de display: none, permite animar la entrada y salida del elemento. Combinada con opacity y una transition corta, genera ese efecto suave de despliegue que normalmente asociarías con librerías de JavaScript.
Este patrón se puede extender a submenús, tooltips, dropdowns de búsqueda y cualquier componente que dependa de mostrar y ocultar contenido según interacción. ¿Has probado ya este enfoque en tu propio navbar? Cuéntame en los comentarios cómo lo adaptaste.