Cómo construir un gráfico Rosquilla semi-círculo Con CSS
Spanish (Español) translation by Elías Nicolás (you can also view the original English article)
Aunque HTML5 Canvas y SVG podrían ser soluciones más elegantes para la construcción de gráficos, en este tutorial aprenderemos cómo construir nuestro propio gráfico de rosquilla con nada más que simple CSS.
Para tener una idea de lo que vamos a crear, eche un vistazo a la demostración incorporada de CodePen a continuación:
Formato HTML
Comenzamos con un marcado muy básico; Una lista desordenada simple con un elemento span dentro de cada uno de los elementos de la lista:
1 |
<ul class="chart-skills"> |
2 |
<li>
|
3 |
<span>CSS</span> |
4 |
</li>
|
5 |
<li>
|
6 |
<span>HTML</span> |
7 |
</li>
|
8 |
<li>
|
9 |
<span>PHP</span> |
10 |
</li>
|
11 |
<li>
|
12 |
<span>Python</span> |
13 |
</li>
|
14 |
</ul>
|
Añadir estilos a la Lista
Con el formato listo, primero aplicamos algunos estilos básicos a la lista desordenada:
1 |
.chart-skills { |
2 |
position: relative; |
3 |
width: 350px; |
4 |
height: 175px; |
5 |
}
|
Luego, vamos a dar a cada uno un pseudo-elemento ::after y a ::before, y los estilamos:
1 |
.chart-skills::before, |
2 |
.chart-skills::after { |
3 |
position: absolute; |
4 |
}
|
5 |
|
6 |
.chart-skills::before { |
7 |
content: ''; |
8 |
width: inherit; |
9 |
height: inherit; |
10 |
border: 45px solid rgba(211,211,211,.3); |
11 |
border-bottom: none; |
12 |
border-top-left-radius: 175px; |
13 |
border-top-right-radius: 175px; |
14 |
}
|
15 |
|
16 |
.chart-skills::after { |
17 |
content: 'Top Skills'; |
18 |
left: 50%; |
19 |
bottom: 10px; |
20 |
transform: translateX(-50%); |
21 |
font-size: 1.1rem; |
22 |
font-weight: bold; |
23 |
color: cadetblue; |
24 |
}
|
Preste atención a los estilos para el pseudo-elemento ::before. Esto nos da nuestro medio círculo.

Hasta ahora, las reglas antes mencionadas nos dan este resultado:
Añadir estilos a los elementos de lista
Ahora vamos a discutir el estilo de los elementos de la lista.
Posicionamiento
Con respecto a la posición de los elementos de la lista, hacemos lo siguiente:
- Colóquelos justo debajo de sus padres y
- Darles estilos apropiados para crear un semicírculo inverso.
Además, un par de cosas valen la pena destacar aquí:
- Los elementos de la lista están posicionados de forma absoluta, por lo que podemos establecer su propiedad
z-index. - Modificamos
el valor predeterminado de la propiedad de
transform-origin(es decir,transform-origin: 50% 50%) de los elementos de la lista. Específicamente, establecemostransform-origin: 50% 0. De esta manera, cuando animamos (rotamos) los elementos, su esquina superior central se convertirá en el centro de rotación.
Estos son los estilos CSS asociados:
1 |
.chart-skills li { |
2 |
position: absolute; |
3 |
top: 100%; |
4 |
left: 0; |
5 |
width: inherit; |
6 |
height: inherit; |
7 |
border: 45px solid; |
8 |
border-top: none; |
9 |
border-bottom-left-radius: 175px; |
10 |
border-bottom-right-radius: 175px; |
11 |
transform-origin: 50% 0; |
12 |
}
|
13 |
|
14 |
.chart-skills li:nth-child(1) { |
15 |
z-index: 4; |
16 |
border-color: green; |
17 |
}
|
18 |
|
19 |
.chart-skills li:nth-child(2) { |
20 |
z-index: 3; |
21 |
border-color: firebrick; |
22 |
}
|
23 |
|
24 |
.chart-skills li:nth-child(3) { |
25 |
z-index: 2; |
26 |
border-color: steelblue; |
27 |
}
|
28 |
|
29 |
.chart-skills li:nth-child(4) { |
30 |
z-index: 1; |
31 |
border-color: orange; |
32 |
}
|
Echa un vistazo a lo que hemos construido hasta ahora en la siguiente visualización:

En la actualidad, el único elemento de lista que es visible es el verde (que tiene z-index: 4;) los otros están debajo de él.
Animaciones
Antes de cubrir los pasos para animar los elementos de nuestra lista, tomemos nota del porcentaje deseado para cada elemento (es decir, cuánto de la rosquilla cada uno cubrirá). Considere la siguiente tabla:
| Lenguaje | Porcentaje |
|---|---|
| CSS | 12 |
| HTML | 32 |
| PHP | 34 |
| Python | 22 |
A continuación, calculamos cuántos grados tenemos para animar (rotar) cada uno de los elementos. Para averiguar el número exacto de grados para cada ítem, multiplicamos su porcentaje por 180° (no 360° porque estamos utilizando un gráfico rosquilla de semicírculo):
| Lenguaje | Porcentaje | Número de grados |
|---|---|---|
| CSS | 12 | 12/100 * 180 = 21.6 |
| HTML | 32 | 32/100 * 180 = 57.6 |
| PHP | 34 | 34/100 * 180 = 61.2 |
| Python | 22 | 22/100 * 180 = 39.6 |
En este punto estamos listos para configurar las animaciones. En
primer lugar, definimos algunos estilos de animación que se comparten
entre todos los elementos, añadiendo algunas reglas a .chart-skills li:
1 |
animation-fill-mode: forwards; |
2 |
animation-duration: .4s; |
3 |
animation-timing-function: linear; |
A continuación, definimos los estilos únicos de animación:
1 |
.chart-skills li:nth-child(1) { |
2 |
z-index: 4; |
3 |
border-color: green; |
4 |
animation-name: rotate-one; |
5 |
}
|
6 |
|
7 |
.chart-skills li:nth-child(2) { |
8 |
z-index: 3; |
9 |
border-color: firebrick; |
10 |
animation-name: rotate-two; |
11 |
animation-delay: .4s; |
12 |
}
|
13 |
|
14 |
.chart-skills li:nth-child(3) { |
15 |
z-index: 2; |
16 |
border-color: steelblue; |
17 |
animation-name: rotate-three; |
18 |
animation-delay: .8s; |
19 |
}
|
20 |
|
21 |
.chart-skills li:nth-child(4) { |
22 |
z-index: 1; |
23 |
border-color: orange; |
24 |
animation-name: rotate-four; |
25 |
animation-delay: 1.2s; |
26 |
}
|
Tenga en cuenta que agregamos un retraso a todos los elementos, excepto para el primero. De esta manera, creamos buenas animaciones secuenciales. Por ejemplo, cuando la animación del primer elemento termina, aparece el segundo elemento, y así sucesivamente.
El siguiente paso es especificar las animaciones reales:
1 |
@keyframes rotate-one { |
2 |
100% { |
3 |
transform: rotate(21.6deg); |
4 |
/**
|
5 |
* 12% => 21.6deg
|
6 |
*/
|
7 |
}
|
8 |
}
|
9 |
|
10 |
@keyframes rotate-two { |
11 |
0% { |
12 |
transform: rotate(21.6deg); |
13 |
}
|
14 |
100% { |
15 |
transform: rotate(79.2deg); |
16 |
/**
|
17 |
* 32% => 57.6deg
|
18 |
* 57.6 + 21.6 => 79.2deg
|
19 |
*/
|
20 |
}
|
21 |
}
|
22 |
|
23 |
@keyframes rotate-three { |
24 |
0% { |
25 |
transform: rotate(79.2deg); |
26 |
}
|
27 |
100% { |
28 |
transform: rotate(140.4deg); |
29 |
/**
|
30 |
* 34% => 61.2deg
|
31 |
* 61.2 + 79.2 => 140.4deg
|
32 |
*/
|
33 |
}
|
34 |
}
|
35 |
|
36 |
@keyframes rotate-four { |
37 |
0% { |
38 |
transform: rotate(140.4deg); |
39 |
}
|
40 |
100% { |
41 |
transform: rotate(180deg); |
42 |
/**
|
43 |
* 22% => 39.6deg
|
44 |
* 140.4 + 39.6 => 180deg
|
45 |
*/
|
46 |
}
|
47 |
}
|
Antes de seguir adelante, veremos brevemente cómo funcionan las animaciones:
El primer elemento pasa de transform: none a transform: rotate (21.6deg).

El
segundo elemento pasa de transform: rotate(21.6deg) (comienza desde
la posición final del primer elemento) a transform: rotate(79.2deg)
(57.6deg + 21.6deg).

El
tercer elemento pasa de transform: rotate(79.2deg) (comienza desde la
posición final del segundo elemento) hasta transform: rotate(140.4deg) (61.2deg + 79.2deg).

El
cuarto elemento pasa de transform: rotate(140.4deg) (comienza desde
la posición final del tercer elemento) hasta transform: rotate(180deg)
(140.4deg + 39.6deg).

¡Ocultar!
Por último, pero no menos importante, para ocultar la mitad inferior del gráfico, tenemos que añadir las siguientes reglas:
1 |
.chart-skills { |
2 |
/* existing rules....*/
|
3 |
|
4 |
overflow: hidden; |
5 |
}
|
6 |
|
7 |
.chart-skills li { |
8 |
/* existing rules....*/
|
9 |
|
10 |
transform-style: preserve-3d; |
11 |
backface-visibility: hidden; |
12 |
}
|
El
valor de propiedad overflow: hidden garantiza que sólo se vea el primer
semicírculo (el creado con el pseudo-elemento ::before). Siéntase libre de eliminar esa propiedad si desea probar la posición inicial de los elementos de la lista.
Las
propiedades de transform-style: preserve-3d y backface-visibility: hidden evitan efectos parpadeantes que pueden ocurrir en
diferentes navegadores debido a animaciones. Si este problema persiste en su navegador, quizás desee probar estas soluciones también.
¡El gráfico está casi listo! Lo único que queda es diseñar las etiquetas del gráfico, lo que haremos en la siguiente sección.
Aquí está la demostración de CodePen que muestra la apariencia actual de nuestro gráfico:
Añadir estilos a las etiquetas
En esta sección, diseñaremos las etiquetas del gráfico.
Posicionamiento
Con respecto a su posición, hacemos lo siguiente:
- Déles la
position: absolutey utilice las propiedadestopeleftpara fijar su posición deseada. - Utilice valores negativos para rotarlos. Por supuesto, estos no son valores aleatorios. De hecho, estos se extraen del último frame del elemento de su padre. Por
ejemplo, la última frame del segundo elemento de lista incluye
transform: rotate(79.2deg), y por lo tanto su etiqueta relacionada tendrátransform: rotate(-79.2deg).
A continuación se muestran los estilos CSS correspondientes:
1 |
.chart-skills span { |
2 |
position: absolute; |
3 |
font-size: .85rem; |
4 |
}
|
5 |
|
6 |
.chart-skills li:nth-child(1) span { |
7 |
top: 5px; |
8 |
left: 10px; |
9 |
transform: rotate(-21.6deg); |
10 |
}
|
11 |
|
12 |
.chart-skills li:nth-child(2) span { |
13 |
top: 20px; |
14 |
left: 10px; |
15 |
transform: rotate(-79.2deg); |
16 |
}
|
17 |
|
18 |
.chart-skills li:nth-child(3) span { |
19 |
top: 18px; |
20 |
left: 10px; |
21 |
transform: rotate(-140.4deg); |
22 |
}
|
23 |
|
24 |
.chart-skills li:nth-child(4) span { |
25 |
top: 10px; |
26 |
left: 10px; |
27 |
transform: rotate(-180deg); |
28 |
}
|
Animaciones
Ahora que hemos colocado las etiquetas, es hora de animarlas. Dos cosas que vale la pena mencionar aquí:
- De forma predeterminada, todas las etiquetas estan ocultas y se hacen visibles a medida que se anima su elemento padre.
- De forma similar a los elementos padres, usamos la propiedad
animation-delaypara crear animaciones secuenciales. Además, agregamos el valor de la propiedadbackface-visibility: hiddenpara asegurar que no haya efectos de parpadeo debido a las animaciones.
Las reglas CSS que se ocupan de la animación de las etiquetas del gráfico se muestran a continuación:
1 |
.chart-skills span { |
2 |
backface-visibility: hidden; |
3 |
animation: fade-in .4s linear forwards; |
4 |
}
|
5 |
|
6 |
.chart-skills li:nth-child(2) span { |
7 |
animation-delay: .4s; |
8 |
}
|
9 |
|
10 |
.chart-skills li:nth-child(3) span { |
11 |
animation-delay: .8s; |
12 |
}
|
13 |
|
14 |
.chart-skills li:nth-child(4) span { |
15 |
animation-delay: 1.2s; |
16 |
}
|
17 |
|
18 |
@keyframes fade-in { |
19 |
0%, |
20 |
90% { |
21 |
opacity: 0; |
22 |
}
|
23 |
100% { |
24 |
opacity: 1; |
25 |
}
|
26 |
}
|
Aquí está el gráfico final:
Soporte del navegador y problemas
En general, la demostración funciona bien en todos los navegadores. Sólo quiero discutir dos pequeños problemas que están relacionados con la propiedad border-radius.
En primer lugar, si tuviera que dar diferentes colores a nuestros artículos, el gráfico podría ser algo como esto:

Observe por ejemplo las esquinas superior e inferior del tercer elemento. Hay dos líneas rojas que vienen del color del borde del cuarto artículo. Podemos ver esas líneas porque el cuarto elemento tiene un color de borde más oscuro en comparación con el tercero. Aunque esto es un tema pequeño, es bueno estar al tanto de ello para elegir colores apropiados para sus propios gráficos.
En segundo lugar, en Safari el gráfico aparece como se ve abajo:

Mira los pequeños espacios que aparecen en el segundo y tercer elementos. Si sabe algo sobre este tema, háganoslo saber en los comentarios a continuación.
Conclusion
En este tutorial, pasamos por el proceso de crear un gráfico rosquilla de semicírculo con CSS puro. Una vez más, como se mencionó en la introducción, existen soluciones potencialmente más potentes (por ejemplo, HTML5 Canvas y SVG) que existen para crear este tipo de cosas. Sin embargo, si quieres construir algo simple y ligero, y disfrutar de un desafío, ¡CSS es el camino a seguir!



