1. Web Design
  2. HTML/CSS
  3. HTML

Construir un panel de administración con CSS y un toque de JavaScript

Scroll to top
Read Time: 25 min

() translation by (you can also view the original English article)

En este tutorial de CSS y JavaScript, crearemos un diseño de panel de administración responsive usando un poco de ambos códigos. Para construirlo, cogeremos prestadas algunas ideas del escritorio de WordPress, como su barra de menú lateral y colapsable.

A lo largo del tutorial con HTML, CSS y JavaScript, nos enfrentaremos a un montón de retos, pero serán unos que resultarán ser una buena práctica para mejorar nuestras habilidades con relación al front-end.

Demostración de panel de administración

Sin más preámbulos, echemos un vistazo a la demostración final del panel de administración. Presta atención a los siguientes aspectos:

  • Pulsa sobre el botón Collapse al final de la barra lateral para ver la navegación colapsable en acción.
  • Pulsa sobre toggle switch al pie de la barra lateral para revelar los temas en colores claros y oscuros del panel de administración.
  • Echa un vistazo a la versión a pantalla completa para jugar con su responsividad.

Envato Elements: Descargas ilimitadas de diseños web

Antes de entrar a fondo en este tutorial, quería mencionar un excelente recurso para aquellos diseñadores web que deseen descargar plantillas y otros recursos digitales. Envato Elements ofrece plantillas CSS, así como fuentes, imágenes, mockups, y mucho más.

Por ejemplo, la plantilla de panel con CSS TailStack te permite implementar exactamente el tipo de panel de administración CSS que vamos a crear en el tutorial de JavaScript y CSS de hoy, sin tener que escribir ni una sola línea de código. Simplemente descárgala y úsala para lo que quieras.

Plantilla de panel de administración con CSS TailStackPlantilla de panel de administración con CSS TailStackPlantilla de panel de administración con CSS TailStack

O, si prefieres aprender cómo codificar tu propio panel de administración con HTML, CSS y JavaScript, continúa leyendo este tutorial.  

1. Empieza con el código HTML del panel

Para empezar con el código HTML del panel necesitamos un SVG, una cabecera, y una sección:

1
<svg style="display:none;">...</svg>
2
<header class="page-header">...</header>
3
<section class="page-content">...</section>

Gráficos SVG

Como puedes imaginar, en cualquier panel de administración necesitaremos un puñado de iconos. Afortunadamente, Envato Elements nos proporciona una colección en continuo crecimiento de útiles iconos vectoriales, así que aprovechemos esta biblioteca y descarga estos iconos de comercio y de panel de administración.

iconos de comercio y de panel de administración disponibles en Evato Elementsiconos de comercio y de panel de administración disponibles en Evato Elementsiconos de comercio y de panel de administración disponibles en Evato Elements
Iconos de comercio y de panel de administración disponibles en Evato Elements

En lugar de incluirlos directamente en el HTML del panel a través de las etiquetas img o svg, vayamos un paso más allá y creemos un "sprite" SVG. Para ello, incluiremos todos los iconos en un contenedor SVG. El contenedor debería estar oculto, así que le aplicaremos display: none. Si no lo ocultásemos, quedaría un área vacía en la parte superior nuestro panel de administración.

Colocaremos cada icono dentro de un elemento symbol con un ID único y un atributo viewBox que dependerá del tamaño del icono. Después renderizaremos el icono deseado cuando lo necesitemos invocando el elemento use (mostraré cómo codificar esto en un minuto).

Además, pondremos nuestro logotipo dentro de este contenedor.

Después, examinaremos como cambian los colores de estos SVG dependiendo del modo del tema, pero por ahora familiaricémonos con el código HTML del panel que vamos a necesitar para el sprite SVG:

1
<svg style="display:none;">
2
  <symbol id="logo" viewBox="0 0 140 59">
3
    <g>
4
      <path d="M6.8 57c0 .4-.1.7-.2.9-.1.2-.3.4-.4.5-.1.1-.4.199-.5.3-.2 0-.3.1-.5.1-.1 0-.3 0-.5-.1-.2 0-.4-.101-.5-.3-.2 0-.4-.2-.5-.4-.1-.2-.2-.5-.2-.9V44.7h-2c-.3 0-.6-.101-.8-.2-.2-.1-.3-.2-.5-.4s-.2-.3-.2-.4v-.4c0-.1 0-.2.1-.399 0-.2.1-.301.2-.4.1-.1.3-.3.5-.4.1 0 .4-.1.7-.1h2.1v-3.5c0-1 .1-1.9.3-2.7C4.1 35 4.5 34.3 5 33.7c.5-.6 1.1-1.1 1.9-1.4.8-.3 1.7-.5 2.7-.5.9 0 1.5.101 1.8.4.3.3.5.6.5 1.1 0 .3-.1.601-.3.9-.2.3-.6.4-1.2.4h-.6c-.6 0-1.1.101-1.5.301-.4.199-.7.5-.9.8C7.2 36 7 36.5 7 37c-.1.5-.1 1-.1 1.6V42h2.7c.3 0 .6.1.8.2.2.1.3.2.5.399.1.101.2.301.2.401 0 .2.1.3.1.4 0 .1 0 .3-.1.399 0 .2-.1.3-.2.4-.1.1-.3.3-.5.399-.2.101-.5.2-.8.2H6.8V57z" />
5
      <path d="M30.4 50.2c0 1.3-.2 2.5-.7 3.5-.5 1.1-1.1 2-1.9 2.8-.8.8-1.8 1.4-2.8 1.8-1.1.4-2.3.601-3.5.601-1.3 0-2.4-.2-3.5-.601-1.1-.399-2-1-2.8-1.8-.8-.8-1.4-1.7-1.9-2.8-.5-1.101-.7-2.2-.7-3.5s.2-2.4.7-3.5c.5-1.101 1.1-2 1.9-2.7.8-.8 1.7-1.4 2.8-1.8 1.1-.4 2.3-.601 3.5-.601 1.3 0 2.4.2 3.5.601 1.1.399 2 1 2.8 1.8.8.8 1.4 1.7 1.9 2.7.5 1.1.7 2.3.7 3.5zm-3.4 0c0-.8-.1-1.5-.4-2.3-.2-.7-.6-1.4-1.1-1.9s-1-1-1.7-1.3c-.7-.3-1.5-.5-2.4-.5s-1.7.2-2.4.5-1.3.8-1.7 1.3c-.5.5-.8 1.2-1.1 1.9-.2.699-.4 1.5-.4 2.3s.1 1.5.4 2.3c.2.7.6 1.4 1.1 1.9.5.6 1 1 1.7 1.3s1.5.5 2.4.5 1.7-.2 2.4-.5 1.3-.8 1.7-1.3c.5-.601.8-1.2 1.1-1.9.3-.7.4-1.5.4-2.3z" />
6
      <path d="M38.1 44.8h.1c.4-.899 1-1.7 1.9-2.3s1.8-.9 2.9-.9c.5 0 1 .101 1.3.301.4.199.6.6.6 1.1 0 .6-.2 1-.6 1.2-.4.2-.8.3-1.4.3h-.2c-1.3 0-2.4.5-3.2 1.4-.8.899-1.3 2.3-1.3 4.1v7c0 .4-.1.7-.2.9-.1.199-.3.399-.4.5-.2.1-.4.199-.5.3-.2 0-.3.1-.5.1-.1 0-.3 0-.5-.1-.2 0-.4-.101-.5-.3-.1-.2-.3-.301-.4-.5C35 57.7 35 57.4 35 57V43.5c0-.4.1-.7.2-.9.1-.199.3-.399.4-.5.2-.1.3-.199.5-.199s.3-.101.5-.101c.1 0 .3 0 .4.101.2 0 .3.1.5.199.2.101.3.301.4.5.1.2.2.5.2.9v1.3z" />
7
      <path d="M49.2 51.3c0 .7.2 1.4.5 2 .3.601.7 1.2 1.2 1.601.5.5 1.1.8 1.7 1.1s1.3.4 2 .4c1 0 1.8-.2 2.5-.5.7-.4 1.2-.801 1.8-1.2.2-.2.4-.3.6-.4.2-.301.3-.301.5-.301.4 0 .7.1 1 .4.3.199.4.6.4 1 0 .1 0 .3-.1.5s-.2.4-.4.7c-1.6 1.7-3.7 2.5-6.3 2.5-1.3 0-2.4-.199-3.5-.6s-2-1-2.8-1.8c-.8-.8-1.4-1.7-1.8-2.7-.4-1.1-.7-2.3-.7-3.6 0-1.301.2-2.5.6-3.5.4-1.101 1-2 1.8-2.801.8-.8 1.7-1.399 2.7-1.8 1-.399 2.2-.6 3.4-.6 2.1 0 3.8.6 5.2 1.8s2.3 2.9 2.6 5.2c0 .3.1.5.1.6v.5c0 1.101-.6 1.7-1.7 1.7H49.2V51.3zm9.9-2.5c0-.7-.1-1.3-.3-1.8-.2-.6-.5-1.1-.9-1.5s-.9-.7-1.4-1c-.6-.2-1.2-.4-2-.4-.7 0-1.4.101-2 .4-.6.2-1.2.6-1.6 1-.5.4-.8.9-1.1 1.5-.3.6-.5 1.2-.5 1.8h9.8z" />
8
      <path d="M77.9 55.1c.399-.3.8-.5 1.199-.5.4 0 .7.101 1 .4.2.3.4.6.4.9 0 .199 0 .5-.1.699a1.856 1.856 0 01-.599.701c-.7.5-1.399.9-2.3 1.2s-1.8.4-2.7.4c-1.3 0-2.5-.2-3.5-.601-1.1-.399-2-1-2.8-1.8s-1.4-1.7-1.8-2.7c-.4-1.1-.7-2.3-.7-3.6s.2-2.5.7-3.601c.4-1.1 1.1-2 1.8-2.8.8-.8 1.7-1.399 2.8-1.8 1.101-.4 2.2-.6 3.5-.6.9 0 1.7.1 2.601.399C78.2 42 79 42.4 79.6 43l.7.7c.101.2.2.5.2.7 0 .399-.1.8-.4 1-.3.3-.6.399-1 .399-.199 0-.399 0-.5-.1-.2-.099-.4-.199-.7-.499-.301-.3-.7-.5-1.2-.7s-1-.3-1.7-.3c-.9 0-1.6.2-2.3.5s-1.2.8-1.7 1.3-.8 1.2-1.1 1.9c-.2.699-.4 1.5-.4 2.3s.1 1.5.3 2.2c.2.699.6 1.3 1 1.899.5.5 1 1 1.7 1.3.7.301 1.4.5 2.3.5.7 0 1.3-.1 1.8-.3.4-.099.9-.299 1.3-.699z" />
9
      <path d="M94.6 56.2h-.1c-.6.899-1.4 1.6-2.3 2.1-.9.5-2 .7-3.3.7-.7 0-1.301-.1-2-.3-.7-.2-1.4-.5-1.9-.9-.6-.399-1.1-.899-1.4-1.6-.4-.7-.6-1.5-.6-2.4 0-1.3.3-2.2 1-3 .7-.7 1.6-1.3 2.7-1.7 1.1-.399 2.3-.6 3.7-.699 1.399-.101 2.8-.2 4.199-.2v-.5c0-1.2-.399-2.101-1.1-2.7s-1.7-.9-3-.9c-.7 0-1.4.101-2 .301-.6.199-1.3.5-1.9 1-.3.199-.699.3-1 .3-.3 0-.6-.101-.899-.4-.2-.2-.4-.6-.4-.899 0-.2.101-.5.2-.7s.3-.4.6-.601c.7-.5 1.601-1 2.5-1.3 1-.3 2-.5 3.2-.5s2.2.2 3.101.5c.899.3 1.6.8 2.199 1.4.601.6 1 1.3 1.301 2.1.3.8.399 1.601.399 2.5V56.9c0 .3-.1.6-.2.899-.1.201-.2.401-.4.501-.2.101-.3.2-.5.2s-.3.1-.4.1c-.1 0-.3 0-.399-.1-.2 0-.301-.1-.5-.2-.201-.1-.301-.3-.401-.5s-.2-.5-.2-.899v-.7h-.2zm-.9-5.5c-.8 0-1.7 0-2.5.1-.9.101-1.7.2-2.4.4s-1.3.5-1.8.9-.7 1-.7 1.7c0 .5.101.9.3 1.2.2.3.5.6.801.8.3.2.699.4 1.1.4.4.1.8.1 1.2.1 1.5 0 2.7-.5 3.5-1.399.8-.9 1.2-2.101 1.2-3.5v-.9h-.7v.199z" />
10
      <path d="M111.4 45.4c-.5-.5-1-.801-1.5-1-.5-.2-1.101-.301-1.601-.301-.399 0-.7 0-1.1.101-.4.1-.7.2-1 .399-.3.2-.5.4-.7.7s-.3.601-.3 1c0 .7.3 1.2.899 1.601.601.3 1.601.6 2.801.899.8.2 1.5.4 2.199.7.7.3 1.301.6 1.801 1s.899.8 1.199 1.4c.301.5.4 1.199.4 1.899 0 1-.2 1.8-.6 2.5-.4.7-.9 1.2-1.5 1.7-.601.4-1.301.7-2.101.9-.8.199-1.6.3-2.399.3-1 0-2-.2-2.9-.5-1-.3-1.8-.8-2.5-1.4-.3-.3-.5-.5-.6-.7-.098-.198-.098-.398-.098-.598 0-.4.101-.8.4-1 .3-.3.6-.4 1-.4.399 0 .8.2 1.2.5.5.5 1.1.801 1.699 1.101.601.3 1.2.399 1.9.399.4 0 .8 0 1.2-.1.399-.1.7-.2 1-.4.3-.199.6-.399.8-.699.2-.301.3-.7.3-1.2 0-.8-.399-1.3-1.1-1.7s-1.8-.7-3.2-1c-.6-.1-1.1-.3-1.7-.5-.6-.2-1.1-.5-1.6-.8s-.8-.8-1.101-1.3c-.3-.5-.399-1.2-.399-2 0-.9.2-1.601.5-2.301.401-.6.801-1.2 1.401-1.6.601-.4 1.2-.7 2-.9.7-.199 1.5-.3 2.301-.3.899 0 1.699.101 2.6.4.8.3 1.6.7 2.2 1.2.3.3.5.5.6.699.101.2.101.4.101.601 0 .399-.101.7-.4 1s-.6.399-1 .399c-.402-.199-.802-.399-1.102-.699z" />
11
      <path d="M126 58.4c-.6.3-1.3.399-2.1.399-1.601 0-2.801-.399-3.601-1.3s-1.2-2.2-1.2-3.9v-9H117.2c-.3 0-.601 0-.8-.1-.2-.1-.4-.2-.5-.3-.101-.101-.2-.3-.2-.4 0-.2-.101-.3-.101-.399 0-.101 0-.2.101-.4 0-.2.1-.3.2-.4.1-.1.3-.3.5-.399.199-.101.5-.2.8-.2h1.899v-3.2c0-.399.101-.7.2-.899.101-.2.3-.4.4-.601.2-.1.399-.2.5-.3.2 0 .3-.1.5-.1.1 0 .3 0 .5.1.2 0 .3.1.5.3.2.101.3.3.399.601.101.199.2.6.2.899V42h3.2c.3 0 .6.1.8.2.2.1.3.2.5.399.102.101.202.301.202.401 0 .2.1.3.1.4 0 .1 0 .3-.1.399 0 .2-.1.3-.2.4-.1.1-.3.3-.5.3-.2.1-.5.1-.8.1h-3.2V53.2c0 1 .2 1.7.5 2.1.4.4.8.601 1.4.601.2 0 .5 0 .7-.101.199-.1.399-.1.6-.1.4 0 .7.1.9.399.199.301.3.601.3.9s-.101.5-.2.7c0 .401-.2.601-.5.701z" />
12
      <path d="M133.2 44.8h.1c.4-.899 1-1.7 1.9-2.3.899-.6 1.8-.9 2.899-.9.5 0 1 .101 1.301.301.4.199.6.599.6 1.099 0 .6-.2 1-.6 1.2-.4.2-.801.3-1.4.3h-.2c-1.3 0-2.399.5-3.2 1.4-.8.899-1.3 2.3-1.3 4.1v7c0 .4-.1.7-.2.9-.1.199-.3.399-.399.5-.101.1-.4.199-.5.3-.2 0-.3.1-.5.1-.101 0-.3 0-.5-.1-.2 0-.4-.101-.5-.3-.2-.101-.3-.301-.4-.5-.1-.2-.2-.5-.2-.9V43.5c0-.4.101-.7.2-.9.101-.199.3-.399.4-.5.2-.1.3-.199.5-.199s.3-.101.5-.101c.1 0 .3 0 .399.101.2 0 .301.1.5.199.2.101.301.301.4.5.1.2.2.5.2.9v1.3z" />
13
    </g>
14
    <g>
15
      <g>
16
        <path fill="#08A6DF" d="M70 32.9c-9.1 0-16.5-7.4-16.5-16.5 0-4.8 2.1-9.3 5.7-12.4.5-.4 1.2-.4 1.6.1.4.5.4 1.2-.1 1.6-3.1 2.7-4.9 6.6-4.9 10.7 0 7.8 6.4 14.2 14.2 14.2s14.2-6.4 14.2-14.2c0-7.8-6.4-14.1-14.2-14.1-1.9 0-3.7.4-5.4 1.1-.6.2-1.3 0-1.5-.6-.2-.6 0-1.3.6-1.5C65.7.4 67.8 0 70 0c9.1 0 16.5 7.4 16.5 16.5S79.1 32.9 70 32.9z" />
17
      </g>
18
      <g>
19
        <path fill="#7C2A8A" d="M70 28.4c-6.6 0-11.9-5.4-11.9-11.9 0-6.6 5.4-11.9 11.9-11.9 5 0 9.5 3.2 11.2 7.9.5 1.3.7 2.6.7 4 0 .6-.5 1.1-1.101 1.1-.6 0-1.1-.5-1.1-1.1 0-1.1-.2-2.2-.601-3.3-1.399-3.8-5-6.4-9.1-6.4-5.3 0-9.6 4.3-9.6 9.6s4.3 9.6 9.6 9.6c.6 0 1.1.5 1.1 1.1.002.8-.498 1.3-1.098 1.3z" />
20
      </g>
21
      <g>
22
        <path fill="#EC1848" d="M70 23.9c-4.1 0-7.4-3.3-7.4-7.4s3.3-7.4 7.4-7.4c.6 0 1.1.5 1.1 1.1 0 .6-.5 1.1-1.1 1.1-2.8 0-5.1 2.3-5.1 5.1s2.3 5.1 5.1 5.1 5.1-2.3 5.1-5.1c0-.6.5-1.1 1.101-1.1.6 0 1.1.5 1.1 1.1.099 4.2-3.201 7.5-7.301 7.5z" />
23
      </g>
24
    </g>
25
  </symbol>
26
  <symbol id="down" viewBox="0 0 16 16">
27
    <polygon points="3.81 4.38 8 8.57 12.19 4.38 13.71 5.91 8 11.62 2.29 5.91 3.81 4.38" />
28
  </symbol>
29
  <symbol id="users" viewBox="0 0 16 16">
30
    <path d="M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,15a7,7,0,0,1-5.19-2.32,2.71,2.71,0,0,1,1.7-1,13.11,13.11,0,0,0,1.29-.28,2.32,2.32,0,0,0,.94-.34,1.17,1.17,0,0,0-.27-.7h0A3.61,3.61,0,0,1,5.15,7.49,3.18,3.18,0,0,1,8,4.07a3.18,3.18,0,0,1,2.86,3.42,3.6,3.6,0,0,1-1.32,2.88h0a1.13,1.13,0,0,0-.27.69,2.68,2.68,0,0,0,.93.31,10.81,10.81,0,0,0,1.28.23,2.63,2.63,0,0,1,1.78,1A7,7,0,0,1,8,15Z" /> 
31
  </symbol>
32
  
33
  <!-- more symbols here -->
34
</svg>

Y realmente, eso es todo lo que necesitamos para crear nuestro sprite SVG.

Cabecera

Pasando a la maquetación de página en HTML del panel de administración, echemos un vistazo a la cabecera del mismo.

Dentro de este diseño, definiremos un elemento nav que servirá de contenedor de los siguientes elementos:

  • El logotipo.
  • El botón Collapse que cambiará el menú para las pantallas de los dispositivos móviles.
  • El menú en sí. Este contendrá los enlaces del menú, dos titulares, un conmutador para el modo claro u oscuro, y el botón para colapsar y expandir. Sería más correcto semánticamente tener dos menús independientes y colocar los titulares fuera de ellos, pero puedes enfocarlo de forma distinta si lo prefieres.

Así es como se verá el HTML del panel en pantallas grandes (>767px):

El diseño de la cabecera

La estructura de la cabecera:

1
<header class="page-header">
2
  <nav>
3
    <a href="#0" aria-label="forecastr logo" class="logo">
4
      <svg width="140" height="49">
5
        <use xlink:href="#logo"></use>
6
      </svg>
7
    </a>
8
    <button class="toggle-mob-menu" aria-expanded="false" aria-label="open menu">
9
      <svg width="20" height="20" aria-hidden="true">
10
        <use xlink:href="#down"></use>
11
      </svg>
12
    </button>
13
    <ul class="admin-menu">
14
      <li class="menu-heading">
15
        <h3>Admin</h3>
16
      </li>
17
      <li>
18
        <a href="#0">
19
          <svg>
20
            <use xlink:href="#pages"></use>
21
          </svg>
22
          <span>Pages</span>
23
        </a>
24
      </li>
25
      
26
      <!-- more list items here -->
27
      
28
      <li>
29
        <div class="switch">
30
          <input type="checkbox" id="mode" checked>
31
          <label for="mode">
32
            <span></span>
33
            <span>Dark</span>
34
          </label>
35
        </div>
36
        <button class="collapse-btn" aria-expanded="true" aria-label="collapse menu">
37
          <svg aria-hidden="true">
38
            <use xlink:href="#collapse"></use>
39
          </svg>
40
          <span>Collapse</span>
41
        </button>
42
      </li>
43
    </ul>
44
  </nav>
45
</header>

Fíjate en dos cosas del anterior código:

  • En como usamos el elemento use para referirnos a los elementos de destino.
  • Los atributos ARIA (aria-expanded, aria-label, aria-hidden) que añadimos a los botones conmutadores. Estos atributos nos ayudarán a hacer que el componente sea un poco más accesible. Más adelante explicaremos cómo codificar esto para que se actualicen sus valores según el estado del botón.

HTML del panel para la sección

La sección contendrá dos secciones anidadas.

1
<section class="page-content">
2
  <!-- two sections here -->
3
</section>

Sección n.º 1

Dentro de la primera sección HTML del panel colocaremos el formulario de búsqueda y alguna información (nombre, avatar, y notificaciones) del usuario que actualmente tenga la sesión iniciada.

Aquí tienes su aspecto en pantallas grandes (>767px):

El diseño de la primera secciónEl diseño de la primera secciónEl diseño de la primera sección

La estructura HTML del panel para esta sección:

1
<section class="search-and-user">
2
  <form>
3
    <input type="search" placeholder="Search Pages...">
4
    <button type="submit" aria-label="submit form">
5
      <svg aria-hidden="true">
6
        <use xlink:href="#search"></use>
7
      </svg>
8
    </button>
9
  </form>
10
  <div class="admin-profile">
11
    <span class="greeting">...</span>
12
    <div class="notifications">
13
      <span class="badge">...</span>
14
      <svg>
15
        <use xlink:href="#users"></use>
16
      </svg>
17
    </div>
18
  </div>
19
</section>

De nuevo, observa que añadimos algunos atributos ARIA al botón de envío (submit).

Sección n.º 2

Dentro de la segunda sección HTML del panel, solo para enriquecer la demostración con algo de contenido ficticio, colocaremos algunos contenedores para diferentes elementos. Normalmente datos tabulados, gráficos, o algún tipo de feed.

”Utiliza un máximo de 5 a 7 distintos widgets para crear una visualización. De otra forma, al usuario le resultará difícil concentrase y obtener una visión global clara.” – Taras Bakusevych

Aquí tienes el aspecto que tendrá la estructura HTML del panel en pantallas grandes (>767px):

El diseño de la segunda secciónEl diseño de la segunda secciónEl diseño de la segunda sección
Según las mejores prácticas de UX, es posible que tú no necesites tantas secciones

Estructura HTML del panel para esta sección:

1
<section class="grid">
2
  <article></article>
3
  <article></article>
4
  <article></article>
5
  <article></article>
6
  <article></article>
7
  <article></article>
8
  <article></article>
9
  <article></article>
10
</section>

2. Tutorial CSS para definir los estilos básicos del panel

Con el código de marcado de nuestro panel de administración, hemos dejado de lado el CSS. El primer paso, como siempre, consiste en especificar algunas variables CSS y algunos estilos habituales de restablecimiento:

1
:root {
2
  --page-header-bgColor: #242e42;
3
  --page-header-bgColor-hover: #1d2636;
4
  --page-header-txtColor: #dde9f8;
5
  --page-header-headingColor: #7889a4;
6
  --page-header-width: 220px;
7
  --page-content-bgColor: #f0f1f6;
8
  --page-content-txtColor: #171616;
9
  --page-content-blockColor: #fff;
10
  --white: #fff;
11
  --black: #333;
12
  --blue: #00b9eb;
13
  --red: #ec1848;
14
  --border-radius: 4px;
15
  --box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.075);
16
  --switch-bgLightModeColor: #87cefa;
17
  --switch-sunColor: gold;
18
  --switch-moonColor: #f4f4f4;
19
  --switch-bgDarkModeColor: #1f1f27;
20
}
21
22
* {
23
  padding: 0;
24
  margin: 0;
25
  box-sizing: border-box;
26
}
27
28
ul {
29
  list-style: none;
30
}
31
32
a,
33
button {
34
  color: inherit;
35
}
36
37
a {
38
  text-decoration: none;
39
}
40
41
button {
42
  background: none;
43
  cursor: pointer;
44
}
45
46
input {
47
  -webkit-appearance: none;
48
}
49
50
[type="checkbox"] {
51
  position: absolute;
52
  left: -9999px;
53
}
54
55
label {
56
  cursor: pointer;
57
}
58
59
button,
60
input {
61
  border: none;
62
}
63
64
svg {
65
  display: block;
66
}
67
68
body {
69
  font: 16px/1.5 "Lato", sans-serif;
70
}

Nota: para simplificar, no explicaré todas las reglas en este tutorial de CSS. Aquí existen casi 550 líneas de CSS para HTML. Si quieres, puedes echar un vistazo a todas ellas haciendo clic en la pestaña CSS de la demostración de nuestro proyecto. Aparte de eso, si estás construyendo un sitio en producción, podría ser más manejable usar un preprocesador como Sass para organizar todos estos estilos del panel en CSS. 

3. Define los principales estilos del panel con CSS

En este punto, ya estamos preparados para centrarnos en los estilos CSS del panel de administración.

Aplicar CSS en el HTML de la cabecera

La cabecera será un elemento con una posición fija. Su anchura será de 220px, y su altura será igual a la altura de la ventana de visualización. En caso de que sus contenidos excedan la altura de la ventana, aparecerá una barra de desplazamiento. 

El elemento nav se comportará como un contenedor flexible con una altura mínima igual al 100%. Recuerda que tiene tres elementos hijo directos:

  1. El logotipo
  2. El botón para cambiar al menú para dispositivos móviles
  3. El menú

El botón conmutador será visible solo en las pantallas pequeñas (<768px). Aquí tienes los estilos del panel en CSS que necesitamos para esto:

1
/*CUSTOM VARIABLES HERE*/
2
3
.page-header {
4
  position: fixed;
5
  top: 0;
6
  left: 0;
7
  right: 0;
8
  bottom: 0;
9
  overflow: auto;
10
  padding-top: 20px;
11
  width: var(--page-header-width);
12
  color: var(--page-header-txtColor);
13
  background: var(--page-header-bgColor);
14
}
15
16
.page-header nav {
17
  display: flex;
18
  flex-direction: column;
19
  min-height: 100%;
20
}
21
22
.page-header .toggle-mob-menu {
23
  display: none;
24
}

Consejo: en caso de que prefieras una cabecera posicionada de forma absoluta que cubra toda la altura de la página, añade los siguientes estilos CSS para HTML:

1
body {
2
  position: relative;
3
}
4
5
.page-header {
6
  position: absolute;
7
  top: 0;
8
  left: 0;
9
  height: 100%;
10
  
11
  /*remove these styles*/
12
  /*position: fixed;

13
  top: 0;

14
  left: 0;

15
  right: 0;

16
  bottom: 0;

17
  overflow: auto;*/
18
}

Estilos CSS del HTML para el menú

El menú servirá como contenedor flexible, y le asignaremos flex: 1, de manera que se expanda y cubra la altura de todo su elemento padre.

Al último elemento de menú le aplicaremos margin-top: auto ya que debería ser posicionado justo en la parte inferior del menú. Este comportamiento quedará más claro cuando la barra de desplazamiento no aparezca. Para probarlo, intenta eliminar algunos elementos de menú o echa un vistazo a la demostración en una pantalla con suficiente altura.

En el HTML del panel, los enlaces y los botones dentro del menú también funcionarán como contenedores flexibles y los elementos que contengan (texto e iconos) deberían estar alineados verticalmente.

Los titulares del menú serán un poco más pequeños en comparación con los otros elementos de menú. Además, aumentaremos el espacio entre sus caracteres.

Aquí tienes una parte del HTML del panel para los elementos del menú:

1
/*CUSTOM VARIABLES HERE*/
2
3
.page-header .admin-menu {
4
  display: flex;
5
  flex-direction: column;
6
  flex-grow: 1;
7
  margin-top: 35px;
8
}
9
10
.page-header .admin-menu li:last-child {
11
  margin-top: auto;
12
  margin-bottom: 20px;
13
}
14
15
.page-header .admin-menu li > * {
16
  width: 100%;
17
  padding: 12px 15px;
18
}
19
20
.page-header .admin-menu .menu-heading h3 {
21
  text-transform: uppercase;
22
  letter-spacing: 0.15em;
23
  font-size: 12px;
24
  margin-top: 12px;
25
  color: var(--page-header-headingColor);
26
}
27
28
.page-header .admin-menu a,
29
.page-header .admin-menu button {
30
  display: flex;
31
  align-items: center;
32
  font-size: 0.9rem;
33
}

Cambiar entre el tema en modo oscuro y el claro

¿Cómo codificar el conmutador? Para construirlo cogeremos prestados unos cuantos estilos de otro tutorial de CSS, el cual explica cómo codificar un conmutador con CSS en HTML. Asegúrate de echarle un vistazo y aprende cómo podemos implementarlo con la técnica de casilla de verificación CSS.

Nuestro panel de administración admitirá dos opciones de tema: la opción clara y otra oscura. Por defecto, la opción activa será la oscura y tendrá el siguiente aspecto: 

El modo oscuro habilitado

Cuando hagamos clic en el conmutador su apariencia cambiará a lo siguiente:

El modo claro habilitado

Aquí tienes los estilos CSS del HTML correspondientes:

1
/*CUSTOM VARIABLES HERE*/
2
3
.switch label {
4
  display: grid;
5
  grid-template-columns: auto auto;
6
  grid-column-gap: 10px;
7
  align-items: center;
8
  justify-content: flex-start;
9
}
10
11
.switch span:first-child {
12
  position: relative;
13
  width: 50px;
14
  height: 26px;
15
  border-radius: 15px;
16
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.4);
17
  background: var(--switch-bgLightModeColor);
18
  transition: all 0.3s;
19
}
20
21
.switch span:first-child::before,
22
.switch span:first-child::after {
23
  content: "";
24
  position: absolute;
25
  border-radius: 50%;
26
}
27
28
.switch span:first-child::before {
29
  top: 1px;
30
  left: 1px;
31
  width: 24px;
32
  height: 24px;
33
  background: var(--white);
34
  z-index: 1;
35
  transition: transform 0.3s;
36
}
37
38
.switch span:first-child::after {
39
  top: 50%;
40
  right: 8px;
41
  width: 10px;
42
  height: 10px;
43
  transform: translateY(-50%);
44
  background: var(--switch-sunColor);
45
  box-shadow: 0 0 4px 2px #ffdb1a;
46
}
47
48
.switch [type="checkbox"]:checked + label span:first-child {
49
  background: var(--switch-bgDarkModeColor);
50
}
51
52
.switch [type="checkbox"]:focus + label span:first-child {
53
  box-shadow: 0 3px 5px rgba(255, 255, 255, 0.25);
54
}
55
56
.switch [type="checkbox"]:checked + label span:first-child::before {
57
  transform: translateX(24px);
58
}
59
60
.switch [type="checkbox"]:checked + label span:first-child::after {
61
  left: 12px;
62
  width: 15px;
63
  height: 15px;
64
  background: transparent;
65
  box-shadow: -2px -5px 0 var(--switch-moonColor);
66
  transform: translateY(-50%) rotate(-72deg);
67
}

Estilos del panel con CSS para el contenido

Recuerda que la sección .page-content contiene dos subsecciones.

Esta sección se colocará a 220px de distancia del lateral izquierdo de la ventana de visualización. Además, le asignaremos width: calc(100% - 220px). Fíjate que el valor de su propiedad left es igual a la anchura de la cabecera. 

Estos son los estilos CSS del HTML en cuestión:

1
/*CUSTOM VARIABLES HERE*/
2
3
.page-content {
4
  position: relative;
5
  left: var(--page-header-width);
6
  width: calc(100% - var(--page-header-width));
7
  min-height: 100vh;
8
  padding: 30px;
9
  color: var(--page-content-txtColor);
10
  background: var(--page-content-bgColor);
11
}

Los estilos CSS del panel para la búsqueda y el usuario

Además, recuerda que la sección .search-and-user contiene dos elementos: el formulario de búsqueda y el perfil del administrador, .admin-profile.

Para su presentación, usaremos CSS Grid. El formulario de búsqueda cubrirá todo el espacio disponible, y habrá un hueco de 50px entre él y sus hermanos. Ambos hermanos estarán verticalmente alineados.

El botón de envío dentro del formulario estará posicionado de forma absoluta. Solo contendrá un icono decorativo, y por tanto necesitamos un atributo ARIA para que los lectores de pantalla lo interpreten y por tanto sea accesible.

.admin-profile contiene dos elementos, se comportará como un contenedor flexible con el contenido alineado verticalmente. El elemento de insignia (un contador) estará posicionado de forma absoluta dentro de su padre con el contenido alineado horizontal y verticalmente.

Aquí tienes parte de los estilos necesarios para esta sección:

1
/*CUSTOM VARIABLES HERE*/
2
3
.search-and-user {
4
  display: grid;
5
  grid-template-columns: 1fr auto;
6
  grid-column-gap: 50px;
7
  align-items: center;
8
  background: var(--page-content-bgColor);
9
  margin-bottom: 30px;
10
}
11
12
.search-and-user form {
13
  position: relative;
14
}
15
16
.search-and-user form button {
17
  position: absolute;
18
  top: 50%;
19
  right: 15px;
20
  transform: translateY(-50%);
21
}
22
23
.search-and-user .admin-profile {
24
  display: flex;
25
  align-items: center;
26
}
27
28
.search-and-user .admin-profile .notifications {
29
  position: relative;
30
}
31
32
.search-and-user .admin-profile .badge {
33
  display: flex;
34
  align-items: center;
35
  justify-content: center;
36
  position: absolute;
37
  top: -10px;
38
  right: -3px;
39
  width: 18px;
40
  height: 18px;
41
  border-radius: 50%;
42
  font-size: 10px;
43
  color: var(--white);
44
  background: var(--red);
45
}

Estilos para la cuadrícula

Para desplegar los artículos de nuestro panel de administración, aprovecharemos CSS Grid. Le asignaremos una altura fija de 300px a todos los artículos. Aparte del primer y el último artículo, los cuales cubrirán toda la anchura del elemento padre, todos los demás formarán parte de un diseño a dos columnas. 

Estos son los estilos correspondientes:

1
/*CUSTOM VARIABLES HERE*/
2
3
.page-content .grid {
4
  display: grid;
5
  grid-template-columns: repeat(2, 1fr);
6
  grid-gap: 30px;
7
}
8
9
.page-content .grid > article {
10
  display: flex;
11
  height: 300px;
12
  background: var(--page-content-blockColor);
13
  border-radius: var(--border-radius);
14
  box-shadow: var(--box-shadow);
15
}
16
17
.page-content .grid > article:first-child,
18
.page-content .grid > article:last-child {
19
  grid-column: 1 / -1;
20
}

4. Tutorial de JavaScript para cambiar la cabecera

Cada vez que hagamos clic en el botón para colapsar y expandir, el estado de la cabecera cambiará. Si está expandido, colapsará (dejando solo sus variantes en forma de iconos de los elementos de menú), y viceversa.

Menú colapsivo con iconos

No olvides que esta funcionalidad estará disponible solo en pantallas con una anchura superior a 767px. Para pantallas inferiores, nuestra cabecera tendrá un diseño distinto, algo que veremos en breve.

Durante el estado colapsado de la cabecera, el elemento body recibe la clase collapsed. En ese momento, ocurren las siguientes cosas:

  • La cabecera se encoge. Su anchura cambia de 220px a 40px.
  • La respuesta a esto es que la sección .page-content crece. En concreto, su anchura cambia de width: calc(100% - 220px) a width: calc(100% - 40px). Además, el valor de su propiedad left cambia a 40px en lugar de 220px.
  • El logo, los titulares del menú, los enlaces de texto del menú, el conmutador del tema, y el texto del botón del menú desaparecen.
  • Los valores de los atributos aria-expanded y aria-label del conmutador se actualizan. Además, sus iconos rotan 180 grados, de modo que aparezca como un icono expandido.

Aquí tienes el código JavaScript que implementa esta funcionalidad:

1
const body = document.body;
2
const collapseBtn = document.querySelector(".admin-menu .collapse-btn");
3
const collapsedClass = "collapsed";
4
5
collapseBtn.addEventListener("click", function() {
6
  body.classList.toggle(collapsedClass);
7
  this.getAttribute("aria-expanded") == "true"
8
    ? this.setAttribute("aria-expanded", "false")
9
    : this.setAttribute("aria-expanded", "true");
10
  this.getAttribute("aria-label") == "collapse menu"
11
    ? this.setAttribute("aria-label", "expand menu")
12
    : this.setAttribute("aria-label", "collapse menu");
13
});

Y los estilos asociados:

1
/*CUSTOM VARIABLES HERE*/
2
3
@media screen and (min-width: 768px) {
4
  .collapsed .page-header {
5
    width: 40px;
6
  }
7
8
  .collapsed .page-header .admin-menu li > * {
9
    padding: 10px;
10
  }
11
12
  .collapsed .page-header .logo,
13
  .collapsed .page-header .admin-menu span,
14
  .collapsed .page-header .admin-menu .menu-heading {
15
    display: none;
16
  }
17
18
  .collapsed .page-header .admin-menu svg {
19
    margin-right: 0;
20
  }
21
22
  .collapsed .page-header .collapse-btn svg {
23
    transform: rotate(180deg);
24
  }
25
26
  .collapsed .page-content {
27
    left: 40px;
28
    width: calc(100% - 40px);
29
  }
30
}

5. Tutorial JavaScript para cambiar el tema

Cada vez que hagamos clic en el botón conmutador, los colores del panel de administración cambiarán.

Botón conmutador del modo oscuro y claroBotón conmutador del modo oscuro y claroBotón conmutador del modo oscuro y claro

Recuerda que inicialmente el modo oscuro estará habilitado. Pero, tan pronto esté activo el modo claro, el elemento html recibirá la clase light-mode.

El html de la clase del modo claroEl html de la clase del modo claroEl html de la clase del modo claro

Esta clase actualizará o sobreescribirá los valores de muchas de las variables CSS (especialmente las relativas al color) y por tanto cambiará la apariencia del tema.

Aquí tienes el código JavaScript necesario:

1
const html = document.documentElement;
2
const switchLabel = document.querySelector(".switch label");
3
const switchLabelText = switchLabel.querySelector("span:last-child");
4
const lightModeClass = "light-mode";
5
6
switchInput.addEventListener("input", function () {
7
  html.classList.toggle(lightModeClass);
8
  if (html.classList.contains(lightModeClass)) {
9
    switchLabelText.textContent = "Light";
10
  } else {
11
    switchLabelText.textContent = "Dark";
12
  }
13
});

Y sus correspondientes estilos:

1
/*CUSTOM VARIABLES HERE*/
2
3
.light-mode {
4
  --page-header-bgColor: #f1efec;
5
  --page-header-bgColor-hover: #b9e4e0;
6
  --page-header-txtColor: #2c303a;
7
  --page-header-headingColor: #979595;
8
  --page-content-bgColor: #fff;
9
  --box-shadow: 0 0 10px -2px rgba(0, 0, 0, 0.25);
10
}
11
12
.light-mode .page-header .admin-menu a:hover,
13
.light-mode .page-header .admin-menu a:focus,
14
.light-mode .page-header .admin-menu button:hover,
15
.light-mode .page-header .admin-menu button:focus {
16
  color: var(--black);
17
}
18
19
.light-mode .page-header .logo svg,
20
.light-mode .page-header .admin-menu a:hover svg,
21
.light-mode .page-header .admin-menu a:focus svg,
22
.light-mode .page-header .admin-menu button:hover svg,
23
.light-mode .page-header .admin-menu button:focus svg {
24
  fill: var(--black);
25
}
26
27
.light-mode .switch [type="checkbox"]:focus + label span:first-child {
28
  box-shadow: 0 3px 5px rgba(0, 0, 0, 0.25);
29
}
30
31
@media screen and (max-width: 767px) {
32
  .light-mode .search-and-user .admin-profile svg {
33
    fill: var(--black);
34
  }
35
}

Mantener el modo seleccionado tras la carga de la página

¡Pero aún no hemos terminado! Sería genial si pudiésemos almacenar el tema que prefiere el usuario y habilitarlo cada vez que visiten el panel de administración. Podemos conseguirlo aprovechando el almacenamiento local.

Almacenar el tema preferido por el usuario en la memoria localAlmacenar el tema preferido por el usuario en la memoria localAlmacenar el tema preferido por el usuario en la memoria local

Veamos el código JavaScript que gestionará esta lógica:

1
...
2
3
if (localStorage.getItem("dark-mode") === "false") {
4
  html.classList.add(lightModeClass);
5
  switchInput.checked = false;
6
  switchLabelText.textContent = "Light";
7
}
8
9
switchInput.addEventListener("input", function () {
10
  if (html.classList.contains(lightModeClass)) {
11
    localStorage.setItem("dark-mode", "false");
12
  } else {
13
    localStorage.setItem("dark-mode", "true");
14
  }
15
});

Desde aquí, puedes ir más allá y asumir cosas más complejas usando la función CSS media prefers-color-scheme. Por ejemplo, si el usuario no ha seleccionado ningún modo, puedes mostrar aquel que respete las preferencias de su sistema operativo. Pero por ahora, mantengamos la simplicidad.

6. Muestra información contextual en los elementos del menú de administración

En este momento, añadamos otra nueva característica a la cabecera colapsable que hemos creado con HTML, CSS y JavaScript.

Tal y como hemos visto en la sección anterior, cuando la cabecera colapsa, el texto de los enlaces del menú desaparece. Esto significa que en ese momento solo serán visibles los iconos SVG. Así que, mostremos un bocadillo de información contextual o "tooltip" que explique qué hace cada enlace.

Para conseguirlo, cada vez que nos situemos sobre un enlace del menú (icono), le añadiremos el atributo title, cuyo valor será el texto de su span. Pero, de nuevo, esto solo debe pasar cuando la cabecera esté colapsada y la anchura de la ventana sea de al menos 768px.

Tooltips de los iconos en el menú colapsado

Este es el correspondiente JavaScript:

1
const body = document.body;
2
const menuLinks = document.querySelectorAll(".admin-menu a");
3
const collapsedClass = "collapsed";
4
5
for (const link of menuLinks) {
6
  link.addEventListener("mouseenter", function () {
7
    if (
8
      body.classList.contains(collapsedClass) &&
9
      window.matchMedia("(min-width: 768px)").matches
10
    ) {
11
      const tooltip = this.querySelector("span").textContent;
12
      this.setAttribute("title", tooltip);
13
    } else {
14
      this.removeAttribute("title");
15
    }
16
  });
17
}

7. Convertirlo en responsive

En pantallas de hasta 767px de anchura, nuestra página tendrá el siguiente aspecto:

The mobile layoutThe mobile layoutThe mobile layout

Es muy distinto a nuestro diseño con barra lateral, ¿verdad? Destaquemos las diferencias más importantes comparadas con la versión de equipos de escritorio:

  • Tanto la cabecera como el contenido de la página (.page-content) tienen un posicionamiento estático (position: static) y una anchura igual al 100% (width: 100%).
  • La dirección flex delm elemento nav cambia de columnrow.
  • El botón para conmutar el menú móvil se hace visible.
  • El menú se posiciona de forma absoluta justo bajo la cabecera y está oculto inicialmente. Será visible cada vez que hagamos clic en el botón conmutador.
  • El botón para colapsar y expandir y el elemento .greeting están ocultos.
  • La sección .search-and-user es posicionada de forma absoluta y colocada justo junto al botón de conmutación del menú móvil.

A continuación puedes ver una parte de los estilos responsive:

1
@media screen and (max-width: 767px) {
2
  .page-header,
3
  .page-content {
4
    position: static;
5
    width: 100%;
6
  }
7
8
  .page-header nav {
9
    flex-direction: row;
10
  }
11
12
  .page-header .toggle-mob-menu {
13
    display: block;
14
  }
15
16
  .page-header .admin-menu {
17
    position: absolute;
18
    left: 98px;
19
    top: 57px;
20
    margin-top: 0;
21
    z-index: 2;
22
    border-radius: var(--border-radius);
23
    background: var(--page-header-bgColor);
24
    visibility: hidden;
25
    opacity: 0;
26
    transform: scale(0.95);
27
    transition: all 0.2s;
28
  }
29
  
30
  .page-header .admin-menu li:last-child button,
31
  .search-and-user .admin-profile .greeting {
32
    display: none;
33
  }
34
35
  .search-and-user {
36
    position: absolute;
37
    left: 131px;
38
    top: 10px;
39
    padding: 0;
40
    grid-column-gap: 5px;
41
    width: calc(100% - 141px);
42
    border-radius: var(--border-radius);
43
    background: transparent;
44
  }
45
}

8. Tutorial JavaScript para conmutar el menú móvil

Completemos este funcional diseño creado con HTML, y CSS con JavaScript. Cada vez que hagamos clic en el botón conmutador, el estado del menú cambiará. Si está expandido, se colapsará, y viceversa.

mobile menu toggle

Durante el estado expandido del menú, el elemento body recibe la clase mob-menu-opened. En ese momento, ocurre lo siguiente:

  • El menú aparece.
  • Los valores de los atributos aria-expandedaria-label del botón conmutador se actualizan. Además, su icono es rotado 180 grados, de modo que ahora tiene un aspecto de icono expandido.

Aquí tienes el código JavaScript necesario:

1
const body = document.body;
2
const toggleMobileMenu = document.querySelector(".toggle-mob-menu");
3
4
toggleMobileMenu.addEventListener("click", function() {
5
  body.classList.toggle("mob-menu-opened");
6
  this.getAttribute("aria-expanded") == "true"
7
    ? this.setAttribute("aria-expanded", "false")
8
    : this.setAttribute("aria-expanded", "true");
9
  this.getAttribute("aria-label") == "open menu"
10
    ? this.setAttribute("aria-label", "close menu")
11
    : this.setAttribute("aria-label", "open menu");
12
});

Y el correspondiente CSS:

1
.page-header .toggle-mob-menu svg {
2
  transition: transform 0.2s;
3
}
4
5
.page-header .admin-menu {
6
  transition: all 0.2s;
7
}
8
9
.mob-menu-opened .toggle-mob-menu svg {
10
  transform: rotate(180deg);
11
}
12
13
.mob-menu-opened .page-header .admin-menu {
14
  transform: scale(1);
15
  visibility: visible;
16
  opacity: 1;
17
}

Conclusiones de este tutorial con HTML, CSS y JavaScript

¡Eso es todo chicos! Hemos conseguido construir un diseño de panel de administración completamente funcional con HTML, CSS y JavaScript. Puedes ampliarlo a partir de esta base para crear cualquier tipo de interfaz de administración. ¡Espero que hayas disfrutado de este viaje tanto como yo!

Una nota sobre la accesibilidad

Solo una última nota: ciertamente no soy un experto en accesibilidad, pero he intentado que esta interfaz sea más accesible añadiendo atributos ARIA. Durante este proceso, he consultado en WordPress y en CodePen. Deben existir otros atributos ARIA que podría haber incluido en el código. Por ejemplo, he excluido el atributo aria-controls, el cual es responsable de identificar el contenido relacionado, pero lo hice porque Aria-Controls es una "caca".

Como paso siguiente, asegúrate de optimizar el código usando un preprocesador y agrupa el código JavaScript en funciones. Y si tienes pensado usar este panel de administración en un proyecto real, ¡me encantaría que me lo comentases! 

Como siempre, ¡gracias por leerme!

Más recursos de interfaz de usuario para un panel de administración

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.