Advertisement
  1. Web Design
  2. HTML/CSS
  3. JavaScript for Designers

Crea un portafolio estático con un gráfico de barras avanzado en CSS

Scroll to top
Read Time: 12 min

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

En una publicación previa, te enseñé cómo crear una hermosa página de portafolio a pantalla completa. Durante ese tutorial, también aprendimos a crear un gráfico de columnas responsivo con CSS. En este tutorial vamos a crear otra atractiva página estática para portafolio, esta vez con un gráfico de barras hecho con CSS puro, ¡y sin utilizar ninguna biblioteca externa de JavaScript, SVG o el elemento canvas!

Lo que vamos a crear

Aquí está el proyecto que crearemos:

Tenemos muchas cosas interesantes que tratar, ¡así que vamos a comenzar!

Nota: Este tutorial asume que posees un buen conocimiento de CSS. Por ejemplo, debes estar familiarizado con el posicionamiento CSS y los conceptos básicos de flexbox.

1. Empieza con el marcado de la página

El marcado de la página consta de una cabecera y tres secciones a pantalla completa:

1
<header class="position-fixed text-lightblue page-header">...</header>
2
<section class="d-flex justify-content-center align-items-center vh-100">...</section>
3
<section class="d-flex vh-100 bg-lightwhite">...</section>
4
<section class="d-flex flex-column justify-content-center align-items-center vh-100 position-relative">...</section>

Nota: además de las clases específicas de los elementos, nuestro marcado contiene una serie de clases de utilidad (auxiliares). Usaremos esta metodología para mantener nuestro CSS lo más DRY (No te repitas, por sus siglas en inglés. También se le conoce como: Una vez y solo una) posible. No obstante, en aras de la legibilidad, dentro del CSS no agruparemos las reglas comunes de CSS.

2. Define algunos estilos básicos

Cumpliendo con lo que acabamos de comentar, primero especificamos algunas reglas de reinicio junto con una serie de clases auxiliares:

1
:root {
2
  --gray: #cbcfd3;
3
  --white: white;
4
  --black: #1a1a1a;
5
  --lightwhite: whitesmoke;
6
  --lightblue: #009dd3;
7
  --peach: #ff9469;
8
  --transition-delay: 0.3s;
9
  --transition-delay-step: 0.3s;
10
  --skills-width: 120px;
11
}
12
13
* {
14
  padding: 0;
15
  margin: 0;
16
  box-sizing: border-box;
17
}
18
19
ul {
20
  list-style: none;
21
}
22
23
a {
24
  text-decoration: none;
25
  color: inherit;
26
}
27
28
.d-block {
29
  display: block;
30
}
31
32
.d-flex {
33
  display: flex;
34
}
35
36
.flex-column {
37
  flex-direction: column;
38
}
39
40
.justify-content-center {
41
  justify-content: center;
42
}
43
44
.justify-content-between {
45
  justify-content: space-between;
46
}
47
48
.align-items-center {
49
  align-items: center;
50
}
51
52
.align-items-end {
53
  align-items: flex-end;
54
}
55
56
.flex-grow-1 {
57
  flex-grow: 1;
58
}
59
60
.vh-100 {
61
  height: 100vh;
62
}
63
64
.position-relative {
65
  position: relative;
66
}
67
68
.position-absolute {
69
  position: absolute;
70
}
71
72
.position-fixed {
73
  position: fixed;
74
}
75
76
.text-center {
77
  text-align: center;
78
}
79
80
.text-gray {
81
  color: var(--gray);
82
}
83
84
.text-lightblue {
85
  color: var(--lightblue);
86
}
87
88
.text-peach {
89
  color: var(--peach);
90
}
91
92
.bg-white {
93
  background: var(--white);
94
}
95
96
.bg-lightwhite {
97
  background: var(--lightwhite);
98
}
99
100
.h1 {
101
  font-size: 2.5rem;
102
}
103
104
.h2 {
105
  font-size: 2rem;
106
}

Las nomenclaturas para nuestras clases auxiliares están inspiradas en los nombres de las clases de Bootstrap 4.

3. Crea la cabecera (header) de la página

La cabecera de la página incluye:

  • El logotipo
  • La navegación principal
The header layoutThe header layoutThe header layout

HTML de la cabecera

1
<header class="position-fixed bg-white page-header">
2
  <nav class="d-flex justify-content-between align-items-center">
3
    <a href="" class="text-peach logo">
4
      <strong>DM</strong>
5
    </a>
6
    <ul class="d-flex">
7
      <li>
8
        <a href="#skills">Skills</a>
9
      </li>
10
      <li>
11
        <a href="#contact">Contact</a>
12
      </li>
13
    </ul>
14
  </nav>
15
</header>

CSS de la cabecera

1
.page-header {
2
  left: 0;
3
  right: 0;
4
  top: 0;
5
  padding: 20px;
6
  z-index: 10;
7
}
8
9
.page-header .logo {
10
  font-size: 1.7rem;
11
}
12
13
.page-header li:not(:last-child) {
14
  margin-right: 20px;
15
}
16
17
.page-header a {
18
  font-size: 1.1rem;
19
}

4. Crea la sección hero

La primera sección de nuestra página incluye:

  • Un encabezado o título
  • Una llamada a la acción

Así es como se verá:

The layout of the first sectionThe layout of the first sectionThe layout of the first section

Sección 1 HTML

1
<section class="d-flex justify-content-center align-items-center vh-100">
2
  <h1 class="text-center h1">...</h1> 
3
  <div class="position-absolute scroll-down">...</div>  
4
</section>

Sección 1 CSS

La llamada a la acción incluye una línea delgada que se anima infinitamente, lo que obliga al usuario a desplazarse hacia abajo para ver qué hay debajo del «pliegue» (que puede existir o no).

Sus estilos:

1
.scroll-down {
2
  left: 50%;
3
  bottom: 0;
4
  transform: translateX(-50%);
5
  text-transform: uppercase;
6
  transition: all 0.5s;
7
}
8
9
.scroll-down.is-hidden {
10
  opacity: 0;
11
  visibility: hidden;
12
}
13
14
.scroll-down::after {
15
  content: '';
16
  display: block;
17
  margin: 3px auto 0;
18
  width: 1px;
19
  height: 60px;
20
  background: var(--black);
21
  transform-origin: bottom;
22
  animation: pulse 3.5s infinite linear;
23
}
24
25
@keyframes pulse {
26
  0% {
27
    transform: scaleY(1);
28
  }
29
  50% {
30
    transform: scaleY(0.65);
31
  }
32
  100% {
33
    transform: scaleY(1);
34
  }
35
}

Sección 1 JavaScript

Al principio, la llamada a la acción será visible. Pero cuando el usuario empiece a desplazarse, desaparecerá. De manera más específica, recibirá la clase is-hidden que acabamos de definir en los estilos anteriores.

Aquí está el código JavaScript que se requiere:

1
const scrollDown = document.querySelector(".scroll-down");
2
3
window.addEventListener("scroll", scrollHandler);
4
5
function scrollHandler() {
6
  window.pageYOffset > 0
7
    ? scrollDown.classList.add("is-hidden")
8
    : scrollDown.classList.remove("is-hidden");
9
}

5. Crea la Sección 2

La segunda sección de nuestra página incluye:

  • Una imagen de fondo
  • El gráfico de barras que muestra las habilidades web

Así es como se ve:

The second section layoutThe second section layoutThe second section layout

Sección 2 HTML

1
<section class="d-flex vh-100 bg-lightwhite" id="skills">
2
  <div class="position-relative flex-grow-1 bg-img"></div>
3
  <div class="d-flex justify-content-center align-items-center flex-grow-1">
4
    <div class="position-relative chart-wrapper">
5
      <ul class="d-flex flex-column chart-skills">
6
        <li class="position-relative">
7
          <span>CSS</span>
8
        </li>
9
        ...
10
      </ul>
11
      <ul class="d-flex position-absolute chart-levels">
12
        <li class="flex-grow-1 position-relative">
13
          <span class="position-absolute">Novice</span>
14
        </li>
15
        ...
16
      </ul> 
17
    </div>
18
  </div>
19
</section>

Estiliza la imagen de fondo

La parte izquierda de esta sección contiene una imagen que fue tomada de los fondos con código de Wordpress (Wordpress Code Backgrounds) en Envato Elements. Nos proporcionará la atmósfera que estamos buscando, a la vez que complementará los colores que usamos en otras partes del diseño:

WordPress Code BackgroundsWordPress Code BackgroundsWordPress Code Backgrounds
Fondos con código de Wordpress

Algunos puntos importantes:

  • La imagen aparecerá en pantallas de más de 900 px
  • usaremos CSS (mediante la propiedad background-image) y no HTML (a través del elemento img) para colocar la imagen dentro de la página. Elegimos este método porque nos brinda una formas fácil de mejorar la apariencia de la imagen. Por ejemplo, la sesgaremos aprovechando su pseudoelemento :: after. Incluso podrías jugar con sus colores utilizando background-blend-mode: luminosity.

Nota: De forma predeterminada, img es un elemento vacío y no tiene pseudoelementos.

Los estilos correspondientes:

1
.bg-img {
2
  background: #fff url(bg-programming.jpg) no-repeat center / cover;
3
}
4
5
.bg-img::after {
6
  content: '';
7
  position: absolute;
8
  top: 0;
9
  bottom: 0;
10
  right: 0;
11
  width: 7rem;
12
  background: var(--lightwhite);
13
  transform: skew(-5deg);
14
  transform-origin: left bottom;
15
}
16
17
@media screen and (max-width: 900px) {
18
  .bg-img {
19
    display: none;
20
  }
21
}

Estiliza el gráfico

En este punto nos concentraremos en la parte más desafiante de nuestra demostración; cómo crear el gráfico de barras.

El gráfico tendrá dos ejes. En el eje y colocaremos las habilidades web (CSS, HTML, JavaScript, Python, Ruby). En el otro eje x colocaremos los niveles de habilidad (Novato, Principiante, Intermedio, Avanzado y Experto).

The chart axesThe chart axesThe chart axes

El eje Y

En términos de marcado, cada habilidad es un elemento de la lista situado dentro de la lista .chart-skills. Después, cada elemento de la lista mantendrá su texto dentro de un elemento span, de esta forma:

1
<ul class="chart-skills">
2
  <li class="position-relative">
3
    <span>CSS</span>
4
  </li>
5
  ...
6
</ul>

Definimos un ancho fijo para el elemento span igual a 120 px. Para evitar la repetición y poder cambiar fácilmente este valor (ya que otros valores dependerán de él), vamos a almacenarlo dentro de una variable CSS:

1
:root {
2
  --skills-width: 120px;
3
}
4
5
.chart-wrapper .chart-skills span {
6
  display: inline-block;
7
  width: var(--skills-width);
8
  padding: 15px;
9
}

Cada elemento de la lista contendrá sus propios pseudoelementos :: before y :: after:

1
/*CUSTOM VARIABLES HERE*/
2
3
.chart-wrapper .chart-skills li::before,
4
.chart-wrapper .chart-skills li::after {
5
  content: '';
6
  position: absolute;
7
  top: 25%;
8
  left: var(--skills-width);
9
  height: 50%;
10
  border-top-right-radius: 10px;
11
  border-bottom-right-radius: 10px;
12
  z-index: 2;
13
}

El pseudoelemento :: after tendrá un color gris claro y un ancho estático que se calcula restando el ancho de span del ancho del elemento de la lista:

1
.chart-wrapper .chart-skills li::after {
2
  width: calc(100% - var(--skills-width));
3
  background: rgba(211, 211, 211, 0.3);
4
}

Así es como se ve:

The after pseudo-elementThe after pseudo-elementThe after pseudo-element

El ancho inicial de todos los pseudoelementos ::before será 0:

1
/*CUSTOM VARIABLES HERE*/
2
3
.chart-wrapper .chart-skills li::before {
4
  width: 0;
5
  background: var(--lightblue);
6
  transition: width 0.65s ease-out;
7
}

Pero tan pronto como el gráfico se haga visible en la ventana gráfica, su anchura será animada y recibirá un valor que será determinado por el nivel de habilidad asociado:

Añadir una clase cuando esté a la vista

Existen múltiples formas de detectar si un elemento está visible en la ventana gráfica o no. Aprovechemos la siguiente función útil extraída de un antiguo, aunque popular, hilo de StackOverflow:

1
function isElementInViewport(el) {
2
  var rect = el.getBoundingClientRect();
3
  return (
4
    rect.top >= 0 &&
5
    rect.left >= 0 &&
6
    rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
7
    rect.right <= (window.innerWidth || document.documentElement.clientWidth)
8
  );
9
}

Si has leído mis tutoriales pasados, quizá recuerdes que también he utilizado esta función para animar una línea de tiempo vertical.

Así que, ¡regresando al trabajo en proceso! Cuando el gráfico se haga visible en la ventana gráfica, le daremos la clase in-view.

Aquí está el código JavaScript que hace este trabajo:

1
const chartWrapper = document.querySelector(".chart-wrapper");
2
3
window.addEventListener("scroll", scrollHandler);
4
5
function scrollHandler() {
6
  if (isElementInViewport(chartWrapper)) chartWrapper.classList.add("in-view");
7
}

Los pseudoelementos :: before deben ser animados de manera secuencial. Para darles la velocidad de transición deseada, utilizaremos otras dos variables CSS junto con la función calc().

Estos son los estilos CSS responsables de mostrar esos pseudoelementos:

1
:root {
2
  ...
3
  --transition-delay: 0.3s;
4
  --transition-delay-step: 0.3s;
5
  --skills-width: 120px;
6
}
7
8
.chart-wrapper.in-view .chart-skills li:nth-child(1)::before {
9
  width: calc(90% - var(--skills-width));
10
  transition-delay: var(--transition-delay);
11
}
12
13
.chart-wrapper.in-view .chart-skills li:nth-child(2)::before {
14
  width: calc(75% - var(--skills-width));
15
  transition-delay: calc(
16
    var(--transition-delay) + var(--transition-delay-step)
17
  );
18
}
19
20
.chart-wrapper.in-view .chart-skills li:nth-child(3)::before {
21
  width: calc(62% - var(--skills-width));
22
  transition-delay: calc(
23
    var(--transition-delay) + var(--transition-delay-step) * 2
24
  );
25
}
26
27
.chart-wrapper.in-view .chart-skills li:nth-child(4)::before {
28
  width: calc(49% - var(--skills-width));
29
  transition-delay: calc(
30
    var(--transition-delay) + var(--transition-delay-step) * 3
31
  );
32
}
33
34
.chart-wrapper.in-view .chart-skills li:nth-child(5)::before {
35
  width: calc(38% - var(--skills-width));
36
  transition-delay: calc(
37
    var(--transition-delay) + var(--transition-delay-step) * 4
38
  );
39
}

Recuerda que Microsoft Edge no soporta las operaciones matemáticas anteriores, por lo que si necesitas admitirlo, solamente pasa algunos valores estáticos, de esta forma:

1
.chart-wrapper.in-view .chart-skills li:nth-child(2)::before {
2
  transition-delay: 0.6s;
3
}
4
5
.chart-wrapper.in-view .chart-skills li:nth-child(3)::before {
6
  transition-delay: 0.9s;
7
}

El eje X

En términos de marcado, cada nivel es un elemento de lista ubicado dentro de la lista .chart-levels. Después, cada elemento de la lista mantendrá su texto dentro de un elemento span, de esta manera:

1
<ul class="d-flex position-absolute chart-levels">
2
  <li class="flex-grow-1 position-relative">
3
    <span class="position-absolute">Novice</span>
4
  </li>
5
  ...
6
</ul>

Algunos puntos a tener en cuenta:

  • Definimos la lista como un contenedor flexible. Además, lo posicionamos de manera absoluta y le damos un margen izquierdo igual al ancho de los spans de la primera lista.
  • Le damos a los elementos de la lista flex-grow: 1 para acrecentarlos y que ocupen todo el espacio disponible.
  • Los spans se colocan en la parte inferior del elemento de la lista principal.
The position of the span elementsThe position of the span elementsThe position of the span elements

Los estilos asociados para esa lista:

1
/*CUSTOM VARIABLES HERE*/
2
3
.chart-wrapper .chart-levels {
4
  left: 0;
5
  bottom: 0;
6
  width: 100%;
7
  height: 100%;
8
  padding-left: var(--skills-width);
9
}
10
11
.chart-wrapper .chart-levels li {
12
  border-right: 1px solid rgba(211, 211, 211, 0.3);
13
}
14
15
.chart-wrapper .chart-levels li:last-child {
16
  border-right: 0;
17
}
18
19
.chart-wrapper .chart-levels span {
20
  bottom: 0;
21
  transform: translateY(50px) rotate(45deg);
22
  padding: 10px;
23
  width: 100%;
24
}

6. Crea la Sección 3

La tercera sección de nuestra página incluye:

  • Un encabezado
  • Un enlace mailto

Así se ve:

The layout of the third sectionThe layout of the third sectionThe layout of the third section

Sección 3 HTML

1
<section class="d-flex flex-column justify-content-center align-items-center vh-100 position-relative" id="contact">
2
  <h2 class="h1">...</h2>
3
  <a href="mailto:hello@digitalmonsters.com" class="text-gray h2">...</a> 
4
</section>

7. Volviéndola responsiva

¡Ya casi terminamos! Como último punto, asegurémonos de que el texto tenga una apariencia sólida en todas las pantallas. Aplicaremos una regla dirigida a pantallas angostas:

1
@media screen and (max-width: 600px) {
2
  html {
3
    font-size: 12px;
4
  }
5
}

Una nota importante aquí es que en nuestros estilos hemos utilizado rem para ajustar el tamaño de las fuentes. Este enfoque es realmente útil porque los tamaños de las fuentes son relativos al elemento raíz (html). Si disminuimos el tamaño de la fuente como en el código anterior, los tamaños de las fuentes ajustadas en rem serán disminuidos de manera dinámica. Por supuesto, también pudimos haber utilizado rems para las otras propiedades CSS.

El estado final de nuestro proyecto:

Conclusión

En este tutorial mejoramos nuestro conocimiento de CSS aprendiendo a crear una atractiva página estática para portafolio. Fuimos un paso más allá y creamos un gráfico de barras responsivo sin utilizar ninguna biblioteca externa de JavaScript, SVG, o el elemento canvas. ¡Únicamente con CSS simple!

Esperemos que este tutorial te haya brindado suficiente inspiración para crear un impresionante sitio para un portafolio. Me encantaría ver tu trabajo, ¡asegúrate de compartirlo con nosotros!

Los próximos pasos

Si quieres mejorar o extender esta demostración, aquí hay dos cosas que puedes hacer:

  • Añadir un desplazamiento suave a los enlaces del menú sin utilizar ninguna biblioteca externa de JavaScript.
  • Usar la API Intersection Observer, que es más eficaz, en lugar del evento scroll para verificar si el gráfico es visible en la ventana gráfica o no.

Como siempre, ¡gracias por leer!

Más proyectos prácticos para aumentar tus habilidades de front-end

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.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.