Sencillo menu responsive

Un menú de navegación con diseño responsive o adaptable, adémas de contener los enlaces de navegación, permite un diseño fluido que se adapta a las diferentes dimensiones de los dispositivos que visualizan su contenido.

En este post crearemos un sencillo menu responsive  con CSS y flexbox, HTML y un poco de Javascript. Existen miles de maneras de hacerlo, esta será solo una, pero puede servir como base para crear un menu más vistoso, con más items, con dropdowns etc.  

Empecemos por ver la estructura HTML.


<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://fonts.googleapis.com/css?family=Lato:300,400" rel="stylesheet">
    <link rel="stylesheet" href="style.css" />
    <title>Menu Responsivo</title>
</head>
<body>
    <header>
        <div class="logo">
            <img src="logo.svg" alt="Page Logo">
        </div>
        <div class="menu">
            <span class="menu-btn">Menu</span>
            <div class="menu-container">
                <ul class="nav">
                    <li class="nav-item">
				        <a href="#" class="nav-link">Inicio</a>
		            </li>
                    <li class="nav-item">
				        <a href="#" class="nav-link">Acerca de</a>
		            </li>
                    <li class="nav-item">
		    		    <a href="#" class="nav-link">Servicios</a>
                    </li>
                    <li class="nav-item">
		    		    <a href="#" class="nav-link">Contacto</a>
		            </li>
                </ul>
            </div>
        </div>
    </header>
    <h1>Menu Responsivo</h1>
</body>
<script>
    var menu = document.querySelector('.menu-container')
    document.querySelector('.menu-btn').addEventListener('click', function(){
        this.innerHTML = (this.innerHTML === 'Menu')? '<-return' : 'Menu'
        this.classList.toggle('anim-btn')
        menu.classList.toggle('show')
    })
</script>
</html>

Repacemos rápidamente el código.
No olvidemos colocar la meta etiqueta viewport, la cual nos permitirá optimizar la vista de la página en dispositivos móviles.


  <meta name="viewport" content="width=device-width, initial-scale=1.0">

Es importante colocar el código CSS en una página externa, para evitar hacer muy extenso el código en una sola página. Dicho esto enlazaremos el código CSS en nuestro HTML, junto con la fuente para el tipo de letra desde Google Fonts.


  <link href="https://fonts.googleapis.com/css?family=Lato:300,400" rel="stylesheet">
  <link rel="stylesheet" href="style.css" />

En este ejemplo encontraremos una disposición típica, un header que contendrá dos elementos div, uno para el logo con una etiqueta de imagen y el otro contendrá los elementos necesarios en el menu. Esta de más decir que esta sera la parte crucial de nuestro código HTML.


<header>
  <div class="logo">
    <img src="logo.svg" alt='Page Logo'>
  </div>
  <div class="menu"></div>
</header>

Dentro del div con la clase menu, que contendrá los elementos de nuestro menu responsive, tenemos un span que tendrá la función de botón, para cuando nuestro menu sea dispuesto en un dispositivo móvil; Junto con un elemento div que servirá como contenedor del menu, sus items y sus links.


        <div class="menu">
            <span class="menu-btn">Menu</span>
            <div class="menu-container">
                <ul class="nav">
                    <li class="nav-item">
		              <a href="#" class="nav-link">Inicio</a>
		            </li>
                    <li class="nav-item">
				        <a href="#" class="nav-link">Acerca de</a>
		            </li>
                    <li class="nav-item">
		    		    <a href="#" class="nav-link">Servicios</a>
                    </li>
                    <li class="nav-item">
		    		    <a href="#" class="nav-link">Contacto</a>
		            </li>
                </ul>
            </div>
        </div>

Para terminar con el código html, tenemos un elemento h1, pero solo como elemento de complemento, no tiene ninguna funcionalidad en el menu.
Veamos ahora el código CSS completo.


*, *:after, *:before{
    padding: 0;
    margin: 0;
}

html,body{
    font-size: 16px;
    font-family: 'lato';
    background-color: #fff;
}

.nav, header ,h1{
    display: flex;
    align-items: center;
    justify-content: center;
}
.menu-btn, .nav-link{
    color: white;
}

header, .menu-container{
    background-color: #ea5106;
}

.menu-btn{
    border: solid .1rem #fff;
    padding: .5rem;
    transition: all .3s ease;
    cursor: pointer;
}
.menu-btn.anim-btn{
    padding: .4rem;
}

header{
    justify-content: space-around;
}

.logo img{
    width: 80px;
}

.nav{
    flex-direction: column;
}

.nav-item{
    list-style: none;
    padding: 1rem;
}

.nav-link{
    text-decoration: none;
    font-size: 1.2rem;
}

.menu-container{
    position: absolute;
    left: -100%;
    width: 10%;
    height: 80vh;
    top: 80px;
    padding-top: 2rem;
    transition: all .7s ease-in-out;
}

.menu-container.show{
    left: 0;
    width: 100%;
}

h1{
    height: 70vh;
}

@media (min-width:768px){
    .menu-btn{
        display: none;
    }
    
    .menu-container{
        position: inherit;
        width: inherit;
        height: inherit;
        padding-top: 0;
    }
    
    .nav{
        flex-direction: row;
    }
    
    .nav-link{
        font-size: 1.2rem;
        font-weight: 400;
    }
}

Ahora veamos de donde sale la magia en el CSS.
A nuestro header, a la lista desordenada y al elemento h1 en su propiedad display le asignamos el atributo flex.


.nav, header ,h1{
    display: flex;
    align-items: center;
    justify-content: center;
}

Haciendo esto que los elementos se presenten como cajas flexibles, ocasionando que sus hijos se posicionen a todo el ancho de su padre. Las dos líneas restantes de esta regla alinean de manera horizontal y verticalmente al centro respectivamente.
Posteriormente para los elementos hijos del header se sobrescribirá la propiedad justify-content: center; por justify-content: space-around; con lo cual el espacio entre los elemento hijos de header, será distribuido uniformemente entre ellos.


header {
    justify-content: space-around;
}

Para los elementos de nuestra lista (los cuales contendrán los enlaces del menu), será necesario mostrarlos en un inicio de manera vertical, para ello bastará agregar a nuestra lista la siguiente propiedad.


.nav{
    flex-direction: column;
}

Para nuestro elemento contenedor del menu, aplicamos la siguiente regla.


.menu-container{
    position: absolute;
    left: -100%;
    width: 10%;
    height: 80vh;
    top: 80px;
    padding-top: 2rem;
    transition: all .7s ease-in-out;
}

Plicamos una disposición absoluta, para desligarlo de su elemento contenedor, dado que en un principio no se visualizará lo colocamos a un 100% a la izquierda con la propiedad left: -100%; y 80 pixeles con respecto al margen superior (para evitar que se encime con el header), con con un ancho del 10% y un alto de 80 porciento con respecto al viewport height; Para terminar agregamos un margen interno de 2rem y una propiedad de transición para el momento de mostrar el menu.

Hasta este punto con las propiedades antes mostradas, el menu no se muestra en pantalla, la magia sucede cuando se pulsa el botón de menu, entonces se agregará una clase al elemento contenedor.
Veamos...


.menu-container.show{
    left: 0;
    width: 100%;
}

Al agregar la clase .show al contenedor, se sobrescriben las propiedades left: -100%; y width: 10%; por left: 0; y width: 100%; provocando que el contenedor del menu se desplace de una zona no visible, al margen izquierdo de la pantalla y agrandando su tamaño al 100% por lo que se visualiza a pantalla completa.

Recordemos que estamos empleando la técnica de first-mobile dando preferencia a dispositivos móviles, entonces estas reglas seran cargadas por al inicio, y al llegar a dispositivos con un mayor tamaño, cargará las siguientes reglas y sobrescribirá las anteriores, gracias a las media queries de CSS.


@media (min-width:768px){
    .menu-btn{
        display: none;
    }
    
    .menu-container{
        position: inherit;
        width: inherit;
        height: inherit;
        padding-top: 0;
    }
    
    .nav{
        flex-direction: row;
    }
    
    .nav-link{
        font-size: 1.2rem;
        font-weight: 400;
    }
}

Como lo mencionamos, las reglas anteriores serán cargadas solamente cuando el tamaño mínimo horizontal de la pantalla sea de 768 pixeles (es decir superior a 768 pixeles). Para iniciar ocultamos el botón del menu.


@media (min-width:768px){
    .menu-btn{
        display: none;
    }
}

Al elemento contenedor del menu lo regresamos a su disposición natural, al igual que su ancho y alto además de quitarle el margen interior superior.


@media (min-width:768px){
    .menu-container{
        position: inherit;
        width: inherit;
        height: inherit;
        padding-top: 0;
    }   
}

Por último a los elementos de nuestra lista (links del menu) le regresamos su disposición horizontal.


@media (min-width:768px){
  .nav{
        flex-direction: row;
    }  
}

Para terminar veamos rápidamente el código Javascript, que en esta ocasión lo incluimos dentro del HTML para fines prácticos


    var menu = document.querySelector('.menu-container')
    document.querySelector('.menu-btn').addEventListener('click', function(){
        this.innerHTML = (this.innerHTML === 'Menu')? '<-return' : 'Menu'
        this.classList.toggle('anim-btn')
        menu.classList.toggle('show')
    })

En una variable llamada menu, guardamos el objeto que apunta a nuestro elemento contenedor del menu


    var menu = document.querySelector('.menu-container')

Seleccionamos nuestro botón del menu, meidante su clase, gracias el metodo addEventListener(), lo ligamos con el evento click y le asignamos una función.


    document.querySelector('.menu-btn').addEventListener('click', function(){
        
    })

Dentro de la función del evento click si nuestro botón tiene como texto Menu entonces lo cambiamos por el de <-Return y viceversa.


    document.querySelector('.menu-btn').addEventListener('click', function(){
        this.innerHTML = (this.innerHTML === 'Menu')? '<-return' : 'Menu'


    })

A nuestro botón le agregamos o quitamos una clase llamada anim-btn, que cumple la función de una pequeña animación...


    document.querySelector('.menu-btn').addEventListener('click', function(){
        this.classList.toggle('anim-btn')
        
    })

Para terminar agregamos o quitamos la clase que contiene las propiedades para mostrar el contenedor de nuestro menu...


    document.querySelector('.menu-btn').addEventListener('click', function(){


        menu.classList.toggle('show')
    })

Con esto terminamos nuestro ejemplo, únicamente detallamos aquí las secciones del código que le dan funcionalidad a nuestro ejemplo, el resto de código no lo mencionamos por brindar estilo, dando por sentado que es fmiliar para el lector.