Cómo hacer un menú off canvas en CSS puro

Ahora que ya han pasado las fiestas y hemos dejado atrás las comilonas, las cenas familiares y con amigos, y las resacas de champán, cava y otras bebidas espiritosas, es momento de dar el siguiente paso tras crear el típico botón estilo hamburguesa y ponernos manos a la obra para darle uso, y que mejor forma que hacerlo con un menú off canvas (menú oculto).

Para ello vamos a hacerlo al estilo Hexagonal Alien (a pelo), usando únicamente CSS, apoyándonos en el uso de una checkbox al igual que hicimos en el post anterior cuando creamos el icono estilo hamburguesa. El menú va ha ser muy sencillito, además voy a explicarlo paso a paso, así que menos cháchara y al lío.

Lo primero de todo es hacernos una idea general de lo que queremos conseguir, ya que un menú off canvas puede hacerse de varias formas. Nosotros vamos a hacerlo moviendo todo el contenido, así que necesitaremos una caja de carácter general donde meterlo todo (luego explicaré el porqué). También otra caja que contendrá el contenido de la web, otra para el menú y como no, la dichosa checkbox.

Como vamos a aprovechar el botón que creamos en el post anterior, tendremos que hacer una pequeña modificación, ya que únicamente necesitamos una checkbox y la del botón no nos sirve donde está colocada. Podemos aprovechar toda la maquetación que hicimos para el botón, pero la checkbox la sacaremos al contenedor de carácter general, e irá justo antes que el contenedor para el contenido y que el menú (también te explicaré el porqué). Dentro del contenedor para el contenido pondremos nuestro icono estilo hamburguesa encargado de hacer que todo se mueva. Con todo esto el HTML nos queda así de sencillo:


<div id="wrapper">
  <input type="checkbox" name="button-flag" value="flag" id="flag">
  <div id="content-wrapper">
    <label id="hamburger" for="flag"><span></span>
    </label>
    <p>Contenido</p>
  </div>
  <div id="menu">
    <a href="#">Enlace1</a>
    <a href="#">Enlace2</a>
    <a href="#">Enlace3</a>
    <a href="#">Enlace4</a>
  </div>
</div>

Ahora vamos con las explicaciones: En primer lugar hemos creado un contenedor de carácter general, su misión es muy sencilla, nos va a ocultar el sobrante cuando movamos el menú y el contenido, mediante la propiedad overflow de CSS, ya que sino se nos creará una barra de desplazamiento inferior.

En segundo lugar hemos creado otro contenedor para el contenido de la página, exceptuando el menú y la checkbox, con la única finalidad de poder desplazar este contenido, un menú que vamos a colocar off canvas poniéndolo en posición absoluta y la checkbox, que como puedes ver va antes que el contenido y el menú.

La explicación para colocar la checkbox de esta forma es bien sencilla, los selectores de CSS funcionan en un único sentido (al igual que CSS), de arriba a abajo y de fuera a dentro, con lo cual necesitamos que esté antes que los elementos con los que vamos a interactuar: menú, contenido y botón.

Lo siguiente es darle forma a los contenedores, y posicionar el menú en función al ancho que vayamos a darle. También daremos estilo a la checkbox (únicamente vamos a hacer que desaparezca) e igualaremos la altura del menú a la del contenido (usando display:table y display:table-cell):


#wrapper {
    position: relative;
    display: table;
    overflow: hidden;
}

#content-wrapper {
    position: relative;
}

#menu {
    position: absolute;
    top: 0;
    left: -18em;
    display: table-cell;
    width: 18em;
    height: 100%;
    background: #314d67;
}

#flag {
    display: none;
}

El siguiente paso es animar el menú, para lo cual hay que tener en cuenta el ancho del mismo, que es la distancia que vamos a mover tanto el menú como el contenido:


#content-wrapper {
    transition: all .25s;
    -webkit-transform: translateX(0);
            transform: translateX(0);
}

#menu {
    transition: all .25s;
    -webkit-transform: translateX(0);
            transform: translateX(0);
}

#flag:checked + #content-wrapper,
#flag:checked ~ #menu {
    -webkit-transform: translateX(18em);
            transform: translateX(18em);
}

La animación es bien sencilla, únicamente movemos el contenido y el menú sobre el eje X cuando tenemos marcada la checkbox y volvemos a la posición inicial cuando está desmarcada. A continuación vamos a maquetar el menú, no es necesario para su funcionamiento, pero ya que estamos vamos a dejarlo presentable:


#menu a:hover {
    transition: background .25s;
    background: #1e3a54;
}

#menu a:before {
    position: absolute;
    top: 0;
    left: 0;
    width: .8em;
    height: 100%;
    content: '';
    transition: -webkit-transform .25s;
    transition: transform .25s;
    -webkit-transform: translateX(-3em);
            transform: translateX(-3em);
    background: #2e6b55;
}

#menu a:hover:before {
    -webkit-transform: translateX(0em);
            transform: translateX(0em);
}

Por último, y esto tampoco es necesario, vamos a cambiar el aspecto del botón que creamos en el post anterior para que no desentone con el aspecto del menú. Además hay que cambiar los selectores, acuérdate que la principio del post cambiamos la checkbox de sitio, quedando el código de esta manera:


#hamburger {
    position: relative;
    display: block;
    width: 3em;
    height: 3em;
    cursor: pointer;
    background: #1e3a54;
}

#hamburger span {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 2em;
    height: .3em;
    margin: auto;
    transition: background 0s .5s;
    background: #fff;
}

#hamburger span:before,
#hamburger span:after {
    position: absolute;
    width: inherit;
    height: inherit;
    content: '';
    transition-delay: .5s, .25s;
    transition-duration: .25s, .25s;
    background: #fff;
}

#hamburger span:before {
    top: -.7em;
    transition-property: top, -webkit-transform;
    transition-property: top, transform;
}

#hamburger span:after {
    bottom: -.7em;
    transition-property: bottom, -webkit-transform;
    transition-property: bottom, transform;
}

#flag:checked + #content-wrapper #hamburger span {
    background: none;
}

#flag:checked + #content-wrapper #hamburger span:before {
    top: 0;
    -webkit-transform: rotate(45deg);
            transform: rotate(45deg);
}

#flag:checked + #content-wrapper #hamburger span:after {
    bottom: 0;
    -webkit-transform: rotate(-45deg);
            transform: rotate(-45deg);
}

#flag:checked + #content-wrapper #hamburger span:before,
#flag:checked + #content-wrapper #hamburger span:after {
    transition-delay: .25s, .5s;
}

Como en el post anterior he creado un pen para que veas como funciona y puedas trastear con él todo lo que quieras: arriba, abajo, a la izquierda, a la derecha, pero nunca adelante ni atrás en el tiempo (eso me lo guardo xD):

See the Pen mVROZx by amram (@amram1977) on CodePen.