Cómo crear el gráfico animado de un termómetro con CSS
() translation by (you can also view the original English article)
Hasta el momento, en nuestra serie de tutoriales de gráficos con CSS, te he enseñado a crear un gráfico de columnas, un gráfico de barras y un gráfico de dona (rosquilla) en semicírculo.
En este tutorial cubriré el proceso para crear el gráfico animado de un «termómetro» con CSS puro; la forma perfecta para rastrear el logro de un solo objetivo.
Lo que crearemos
Aquí está el gráfico que crearemos (presiona reload [recargar] para verlo animado):
1. Empieza con los datos
Para los fines de esta demostración, necesitaremos algunos datos. Trabajemos con algunas cifras ficticias que describen la financiación de una organización benéfica a lo largo de los años:
Año | Financiación |
---|---|
2018 | €95,000 |
2016 | €72,000 |
2015 | €50,000 |
2012 | €35,000 |
2010 | €15,000 |
Como habrás adivinado, nuestro reto es visualizar estos datos en un gráfico de termómetro. Nos inspiraremos visualmente en esos problemáticos bienhechores Extinction Rebellion (si no has oído hablar de ellos, ¡mira lo que están haciendo!).
2. Especifica el marcado de la página
Especificaremos un elemento de envoltura que contiene tres listas:
- La primera lista establece el rango del eje Y. Si miras más de cerca los datos de la tabla anterior, verás que la segunda columna incluye valores de hasta 95,000. Considerando esto, definiremos seis valores de 0 a 100,000 con un tamaño de paso de 20,000. Por lo tanto, los valores del eje y serán 0, 2,000, 40,000, 60,000, 80,000 y 100,000.
- La segunda lista establece los datos del eje X. Estos se extraen de la primera columna de nuestra tabla, de menor a mayor. Pero su orden no importa porque los posicionaremos de manera absoluta. No obstante, observa en el marcado a continuación, que un elemento de la lista contiene el mismo año dos veces. Podríamos haber omitido poner el año como nodo de texto del elemento. Pero es importante almacenar este valor en el atributo
data-year
. Como veremos posteriormente, pasaremos el valor de este atributo al pseudoelemento relacionado:: before
. - La tercera lista agrupa los datos de la tabala mencionada anteriormente, desde el valor de financiación más alto hasta el más bajo. Nuevamente, aquí el orden no es importante. También puedes mostrarlos desde el más bajo al más alto. Esto depende completamente de ti.
Aquí está el marcado que se requiere:
1 |
<div class="chart-wrapper"> |
2 |
<ul class="chart-y"> |
3 |
<li>€100,000</li> |
4 |
... |
5 |
</ul>
|
6 |
<ul class="chart-x"> |
7 |
<li data-year="2010">2010</li> |
8 |
... |
9 |
</ul>
|
10 |
<ul class="chart-labels"> |
11 |
<li>2018 - €95,000</li> |
12 |
... |
13 |
</ul>
|
14 |
</div>
|
3. Define algunos estilos básicos
A continuación, configuraremos algunas variables CSS y algunos estilos comunes de restablecimiento:
1 |
@font-face { |
2 |
font-family: "Cheddar Gothic Sans"; |
3 |
src: url("cheddargothic-sans-webfont.woff2") format("woff2"), |
4 |
url("cheddargothic-sans-webfont.woff") format("woff"); |
5 |
}
|
6 |
|
7 |
:root { |
8 |
--brand-color: #21a73d; |
9 |
--chart-bg-color: rgba(211, 211, 211, .3); |
10 |
--chart-line-color: black; |
11 |
--chart-x-color: white; |
12 |
--line-color1: crimson; |
13 |
--line-color2: gold; |
14 |
--line-color3: firebrick; |
15 |
--line-color4: orange; |
16 |
--line-color5: darkblue; |
17 |
--black: #2d2929; |
18 |
--white: white; |
19 |
--transition-delay: 0.6s; |
20 |
--transition-delay-step: 0.6s; |
21 |
}
|
22 |
|
23 |
* { |
24 |
padding: 0; |
25 |
margin: 0; |
26 |
box-sizing: border-box; |
27 |
}
|
28 |
|
29 |
ul { |
30 |
list-style: none; |
31 |
}
|
32 |
|
33 |
a { |
34 |
text-decoration: none; |
35 |
color: inherit; |
36 |
}
|
37 |
|
38 |
body { |
39 |
font: 1rem/1.2 Georgia, serif; |
40 |
padding-top: 70px; |
41 |
background: var(--brand-color); |
42 |
}
|
Aquí no ocurre nada realmente espectacular, pero tal vez una cosa a tener en cuenta es que, además de la conocida fuente serif del sistema «Georgia», también he utilizado la fuente premium «Cheddar Gothic» de Envato Elements.



Nota: Para simplificar, no explicaré detalladamente todas las reglas de CSS en el tutorial. Puedes revisar el resto de ellas haciendo clic en la pestaña CSS del proyecto de demostración.
4. Estiliza el gráfico
La envoltura del gráfico será un contenedor de cuadrícula con contenido centrado de manera horizontal. Cada columna (lista) tendrá su ancho predeterminado, con un espacio de 4rem entre ellas.



Aquí está el CSS relacionado:
1 |
.chart-wrapper { |
2 |
display: grid; |
3 |
justify-content: center; |
4 |
grid-column-gap: 4rem; |
5 |
grid-template-columns: auto auto auto; |
6 |
}
|
El eje Y
La primera lista que contiene los datos del eje Y también se comportará como un contenedor de cuadrícula. Habrá un espacio de 3rem entre los elementos:

Aquí está el CSS asociado:
1 |
.chart-wrapper .chart-y { |
2 |
display: grid; |
3 |
grid-row-gap: 3rem; |
4 |
}
|
El eje X
La segunda lista que incluye los datos del eje X tendrá un ancho fijo de 50px. Además, le daremos algunos estilos adicionales para que tenga la apariencia de un termómetro real:

Los estilos correspondientes son los siguientes:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.chart-wrapper .chart-x { |
4 |
position: relative; |
5 |
width: 50px; |
6 |
border-radius: 25px; |
7 |
border: 8px solid var(--chart-line-color); |
8 |
background: var(--chart-bg-color); |
9 |
overflow: hidden; |
10 |
}
|
Los elementos de la lista estarán posicionados de manera absoluta con una altura inicial de 0 por defecto, por lo tanto, serán invisibles. Otra cosa que hacemos es establecer su color de primer plano como transparente. En tal caso, el texto que contiene el año no será visible:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.chart-wrapper .chart-x li { |
4 |
position: absolute; |
5 |
left: 0; |
6 |
bottom: 0; |
7 |
width: 100%; |
8 |
height: 0; |
9 |
color: transparent; |
10 |
border-bottom-left-radius: inherit; |
11 |
border-bottom-right-radius: inherit; |
12 |
background: var(--chart-x-color); |
13 |
transition: height 0.5s ease-out; |
14 |
}
|
Para el siguiente paso, especificaremos algunos estilos para su pseudoelemento :: before
. Recuerda que su contenido incluirá el valor del atributo data-year
asociado (ve el marcado anterior). Además, estará oculto al principio:

Los estilos requeridos:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.chart-wrapper .chart-x li::before { |
4 |
content: attr(data-year); |
5 |
position: absolute; |
6 |
top: 0; |
7 |
left: 0; |
8 |
right: 0; |
9 |
z-index: 100; |
10 |
border-top: 4px solid; |
11 |
width: 20px; |
12 |
opacity: 0; |
13 |
padding-left: 3px; |
14 |
color: var(--black); |
15 |
font-size: 0.75rem; |
16 |
transition: opacity 0.5s ease-out; |
17 |
}
|
18 |
|
19 |
.chart-wrapper .chart-x li:nth-child(1)::before { |
20 |
border-color: var(--line-color1); |
21 |
}
|
22 |
|
23 |
.chart-wrapper .chart-x li:nth-child(2)::before { |
24 |
border-color: var(--line-color2); |
25 |
}
|
26 |
|
27 |
.chart-wrapper .chart-x li:nth-child(3)::before { |
28 |
border-color: var(--line-color3); |
29 |
}
|
30 |
|
31 |
.chart-wrapper .chart-x li:nth-child(4)::before { |
32 |
border-color: var(--line-color4); |
33 |
}
|
34 |
|
35 |
.chart-wrapper .chart-x li:nth-child(5)::before { |
36 |
border-color: var(--line-color5); |
37 |
}
|
Anima los elementos
Tan pronto como se cargue la página, la altura de los elementos de la lista se animará y recibirá un valor que será determinado por el valor de financiación asociado (mira la tabla anterior). Por ejemplo, un valor de 15,000 corresponde a height: 15%
. Además de los elementos de la lista, también se animará su pseudoelemento :: before
, aunque las animaciones no se ejecutarán simultáneamente. Primero el elemento se hará visible, luego su pseudoelemento.
Ahora vamos a convertir todos los requisitos anteriores en código.
Cuando la página se cargue, primero añadiremos la clase loaded
a body
:
1 |
window.addEventListener("load", () => { |
2 |
document.body.classList.add("loaded"); |
3 |
});
|
En ese punto los elementos pueden ser animados de manera secuencial. Para lograrlo, aprovecharemos una técnica utilizada en mis anteriores demostraciones de gráficos. Definiremos dos variables CSS que determinan la velocidad de transición (siéntete libre de cambiarlas si es necesario) y las combinaremos con la función calc()
.
Aquí están los estilos CSS responsables de revelar los elementos del eje X:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.loaded .chart-wrapper .chart-x li:nth-child(1) { |
4 |
height: 15%; /*represents €15,000*/ |
5 |
transition-delay: var(--transition-delay); |
6 |
}
|
7 |
|
8 |
.loaded .chart-wrapper .chart-x li:nth-child(2) { |
9 |
height: 35%; /*represents €35,000*/ |
10 |
transition-delay: calc( |
11 |
var(--transition-delay) + var(--transition-delay-step) |
12 |
);
|
13 |
}
|
14 |
|
15 |
.loaded .chart-wrapper .chart-x li:nth-child(3) { |
16 |
height: 50%; /*represents €50,000*/ |
17 |
transition-delay: calc( |
18 |
var(--transition-delay) + var(--transition-delay-step) * 2 |
19 |
);
|
20 |
}
|
21 |
|
22 |
.loaded .chart-wrapper .chart-x li:nth-child(4) { |
23 |
height: 72%; /*represents €72,000*/ |
24 |
transition-delay: calc( |
25 |
var(--transition-delay) + var(--transition-delay-step) * 3 |
26 |
);
|
27 |
}
|
28 |
|
29 |
.loaded .chart-wrapper .chart-x li:nth-child(5) { |
30 |
height: 95%; /*represents €95,000*/ |
31 |
transition-delay: calc( |
32 |
var(--transition-delay) + var(--transition-delay-step) * 4 |
33 |
);
|
34 |
}
|
Como ya he mencionado en las anteriores demostraciones de gráficos, Microsoft Edge no es compatible con estas operaciones matemáticas, por lo que si necesitas admitirlo, deberás pasar algunos valores estáticos, como este:
1 |
.loaded .chart-wrapper .chart-x li:nth-child(2) { |
2 |
height: 15%; |
3 |
transition-delay: 1.2s; |
4 |
}
|
5 |
|
6 |
.loaded .chart-wrapper .chart-x li:nth-child(3) { |
7 |
height: 35%; |
8 |
transition-delay: 1.8s; |
9 |
}
|
Nota: Podría haber definido la propiedad transition-delay
fuera de la clase loaded
de esta forma:
1 |
.chart-wrapper .chart-x li:nth-child(1) { |
2 |
transition-delay: var(--transition-delay); |
3 |
}
|
En cualquier caso, el resultado será el mismo; la animación no se ejecutará hasta que la página se cargue. Únicamente elegí incluir esa propiedad dentro de la clase loaded
porque quería limitar el código CSS y agrupar las propiedades que comparten el mismo selector, por ejemplo esas dos:
1 |
.loaded .chart-wrapper .chart-x li:nth-child(1) { |
2 |
height: 15%; /*represents €15,000*/ |
3 |
}
|
4 |
|
5 |
.chart-wrapper .chart-x li:nth-child(1) { |
6 |
transition-delay: var(--transition-delay); |
7 |
}
|
Como ya se comentó en los pasos anteriores, el pseudoelemento ::before
también debería aparecer en la carga de la página:
1 |
.loaded .chart-wrapper .chart-x li::before { |
2 |
opacity: 1; |
3 |
}
|
Pero con un retraso aún mayor:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.chart-wrapper .chart-x li:nth-child(1)::before { |
4 |
transition-delay: calc( |
5 |
var(--transition-delay) + var(--transition-delay-step) |
6 |
);
|
7 |
}
|
8 |
|
9 |
.chart-wrapper .chart-x li:nth-child(2)::before { |
10 |
transition-delay: calc( |
11 |
var(--transition-delay) + var(--transition-delay-step) * 2 |
12 |
);
|
13 |
}
|
14 |
|
15 |
.chart-wrapper .chart-x li:nth-child(3)::before { |
16 |
transition-delay: calc( |
17 |
var(--transition-delay) + var(--transition-delay-step) * 3 |
18 |
);
|
19 |
}
|
20 |
|
21 |
.chart-wrapper .chart-x li:nth-child(4)::before { |
22 |
transition-delay: calc( |
23 |
var(--transition-delay) + var(--transition-delay-step) * 4 |
24 |
);
|
25 |
}
|
26 |
|
27 |
.chart-wrapper .chart-x li:nth-child(5)::before { |
28 |
transition-delay: calc( |
29 |
var(--transition-delay) + var(--transition-delay-step) * 5 |
30 |
);
|
31 |
}
|
Las etiquetas
La tercera lista que incluye los datos de la tabla estará oculta de forma predeterminada.

Pero en el momento en que las animaciones de los gráficos finalicen, aparecerá:
1 |
.chart-wrapper .chart-labels { |
2 |
opacity: 0; |
3 |
transition: opacity .6s 3.8s; |
4 |
}
|
5 |
|
6 |
.loaded .chart-wrapper .chart-labels { |
7 |
opacity: 1; |
8 |
}
|
Observa el valor de la propiedad transition-delay
que se establece en 3.8s. Utilizaremos este valor porque la propiedad transition-delay
del último elemento :: before
del eje x se establece en 3.6s:



Por último, usaremos el pseudoelemento ::before
de su lista de elementos para crear el colorido rectángulo que aparece en la esquina izquierda de cada elemento:

Aquí están los estilos que se necesitan para eso:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.chart-wrapper .chart-labels li::before { |
4 |
content: ''; |
5 |
display: inline-block; |
6 |
vertical-align: middle; |
7 |
width: 20px; |
8 |
height: 20px; |
9 |
margin-right: 10px; |
10 |
}
|
11 |
|
12 |
.chart-wrapper .chart-labels li:nth-child(1)::before { |
13 |
background: var(--line-color5); |
14 |
}
|
15 |
|
16 |
.chart-wrapper .chart-labels li:nth-child(2)::before { |
17 |
background: var(--line-color4); |
18 |
}
|
19 |
|
20 |
.chart-wrapper .chart-labels li:nth-child(3)::before { |
21 |
background: var(--line-color3); |
22 |
}
|
23 |
|
24 |
.chart-wrapper .chart-labels li:nth-child(4)::before { |
25 |
background: var(--line-color2); |
26 |
}
|
27 |
|
28 |
.chart-wrapper .chart-labels li:nth-child(5)::before { |
29 |
background: var(--line-color1); |
30 |
}
|
¡Así se ve nuestro proyecto final!
Conclusión
¡Eso es todo, amigos! En este tutorial, creamos el gráfico animado de un termómetro con CSS puro. Espero que este ejercicio haya sido interesante y que los desafíe a crear sus propios gráficos con CSS.
Como siempre, ¡gracias por leer!
Más tutoriales sobre gráficos y animación
- Animación CSSRealza la manera en que se carga una página web con animaciones CSSGeorge Martsoukos
- CSSCómo crear una página estática para portafolio con CSS y JavaScriptGeorge Martsoukos
- CSSCrea un portafolio estático con un gráfico de barras avanzado en CSSGeorge Martsoukos
- CSSCómo crear un gráfico de dona (rosquilla) en semicírculo con CSSGeorge Martsoukos