Gráficos en CSS: cómo crear un organigrama horizontal
() translation by (you can also view the original English article)
En un tutorial anterior, aprendimos cómo crear un organigrama vertical usando solamente CSS. Hoy, a petición de algunos, analizaremos el proceso de construcción de su versión horizontal correspondiente.
Y ahora viene lo bueno: vamos a crear este nuevo gráfico sin cambiar una sola línea del marcado del original; solo vamos a modificar los estilos.
Ten en cuenta que, ya que el concepto sigue siendo el mismo, vamos a tomar algunas partes del otro tutorial. ¿Estás listo para poner a prueba tus habilidades de CSS nuevamente?
El organigrama que vamos a crear
Este es el gráfico en CSS que vamos a crear:
En pantallas de hasta 1300px de ancho, el gráfico se mostrará mediante el uso de un diseño vertical. En pantallas más grandes, sus datos se mostrarán horizontalmente. Asegúrate de verificar este comportamiento abriendo la demostración en una pantalla grande y cambiando el tamaño de la ventana de tu navegador.
1. Especifica el contenedor
Nuestro gráfico residirá en el interior de un contenedor:
1 |
<div class="container"> |
2 |
<!-- Chart goes here -->
|
3 |
</div>
|
2. Define algunos estilos básicos
Antes de examinar sus niveles, vamos a crear algunas reglas de restablecimiento y algunas clases auxiliares:
1 |
:root { |
2 |
--level-1: #8dccad; |
3 |
--level-2: #f5cc7f; |
4 |
--level-3: #7b9fe0; |
5 |
--level-4: #f27c8d; |
6 |
--black: black; |
7 |
}
|
8 |
|
9 |
* { |
10 |
padding: 0; |
11 |
margin: 0; |
12 |
box-sizing: border-box; |
13 |
}
|
14 |
|
15 |
ol { |
16 |
list-style: none; |
17 |
}
|
18 |
|
19 |
body { |
20 |
margin: 50px 0 100px; |
21 |
text-align: center; |
22 |
font: 20px/1.5 "Inter", sans-serif; |
23 |
}
|
24 |
|
25 |
h1, |
26 |
h2, |
27 |
h3, |
28 |
h4 { |
29 |
font-size: inherit; |
30 |
}
|
31 |
|
32 |
.container { |
33 |
max-width: 800px; |
34 |
padding: 0 10px; |
35 |
margin: 0 auto; |
36 |
display: grid; |
37 |
align-items: center; |
38 |
justify-content: center; |
39 |
grid-column-gap: 20px; |
40 |
grid-template-columns: auto auto; |
41 |
}
|
42 |
|
43 |
.rectangle { |
44 |
position: relative; |
45 |
padding: 20px; |
46 |
width: 200px; |
47 |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); |
48 |
}
|
Observa la clase rectangle
. Vamos a agregar esta clase a cada nodo o elemento de nuestro gráfico.
Además, presta atención a los estilos del contenedor. Gracias a CSS Grid, podemos dividir el gráfico en dos partes. La primera parte incluirá al primer nivel, mientras que la segunda incluirá a todos los demás. Además, asignaremos al contenedor un ancho máximo que surtirá efecto solamente en pantallas más pequeñas.



Nota: en aras de la simplicidad, no he optimizado el CSS. Esto te ayudará a comprender mejor los estilos de cada nivel.
3. Especifica los niveles
En este punto veremos más de cerca los niveles del gráfico. Como recordarás del tutorial anterior, tendremos cuatro niveles:



Nivel #1
El primer nivel solamente incluirá un único nodo:

HTML
Para describirlo usaremos una etiqueta h1
, ya que es la parte más importante de nuestro gráfico:
1 |
<h1 class="level-1 rectangle">...</h1> |
CSS
Usaremos su pseudoelemento ::before
para crear una relación entre el primer y el segundo nivel:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-1 { |
4 |
background: var(--level-1); |
5 |
}
|
6 |
|
7 |
.level-1::before { |
8 |
content: ""; |
9 |
position: absolute; |
10 |
top: 50%; |
11 |
left: 100%; |
12 |
transform: translateY(-50%); |
13 |
width: 20px; |
14 |
height: 2px; |
15 |
background: var(--black); |
16 |
}
|
Nivel #2
El segundo nivel estará compuesto por dos nodos:

HTML
Para describirlo, usaremos una lista ordenada con dos elementos de lista. Cada elemento de lista contendrá un elemento h2
:
1 |
<ol class="level-2-wrapper"> |
2 |
<li>
|
3 |
<h2 class="level-2 rectangle">...</h2> |
4 |
</li>
|
5 |
<li>
|
6 |
<h2 class="level-2 rectangle">...</h2> |
7 |
</li>
|
8 |
</ol>
|
CSS
Cada elemento de lista actuará como un contenedor de cuadrículas con dos columnas. Colocaremos el contenido de la primera en el borde superior del eje de la columna, mientras que el contenido de la última se encontrará en el borde inferior del eje de la columna.
También definiremos los pseudoelementos ::before
y ::after
de los elementos h2
. Su trabajo será crear las relaciones entre los niveles adyacentes:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-2-wrapper { |
4 |
position: relative; |
5 |
padding-left: 20px; |
6 |
border-left: 2px solid var(--black); |
7 |
}
|
8 |
|
9 |
.level-2-wrapper::before { |
10 |
display: none; |
11 |
content: ""; |
12 |
position: absolute; |
13 |
top: -20px; |
14 |
left: 10px; |
15 |
width: 2px; |
16 |
height: calc(100% + 40px); |
17 |
background: var(--black); |
18 |
}
|
19 |
|
20 |
.level-2-wrapper::after { |
21 |
display: none; |
22 |
content: ""; |
23 |
position: absolute; |
24 |
left: 10px; |
25 |
bottom: -20px; |
26 |
width: calc(100% - 10px); |
27 |
height: 2px; |
28 |
background: var(--black); |
29 |
}
|
30 |
|
31 |
.level-2-wrapper > li { |
32 |
position: relative; |
33 |
display: grid; |
34 |
align-items: flex-start; |
35 |
grid-column-gap: 20px; |
36 |
grid-template-columns: auto auto; |
37 |
}
|
38 |
|
39 |
.level-2-wrapper > li:last-child { |
40 |
margin-top: 100px; |
41 |
align-items: flex-end; |
42 |
}
|
43 |
|
44 |
.level-2 { |
45 |
background: var(--level-2); |
46 |
}
|
47 |
|
48 |
.level-2::before { |
49 |
content: ""; |
50 |
position: absolute; |
51 |
top: 50%; |
52 |
right: 100%; |
53 |
transform: translateY(-50%); |
54 |
width: 20px; |
55 |
height: 2px; |
56 |
background: var(--black); |
57 |
}
|
58 |
|
59 |
.level-2::after { |
60 |
content: ""; |
61 |
position: absolute; |
62 |
top: 50%; |
63 |
left: 100%; |
64 |
transform: translateY(-50%); |
65 |
width: 20px; |
66 |
height: 2px; |
67 |
background: var(--black); |
68 |
}
|
Nivel #3
El tercer nivel incluirá cuatro nodos.
Asociaremos los dos primeros nodos con el primer nodo del segundo nivel, mientras que los dos últimos estarán asociados con su segundo nodo:

HTML
Todavía en el interior de la lista inicial, en donde reside el segundo nivel, definiremos dos listas nuevas. Cada una de ellas contendrá dos elementos de lista. Para cada elemento especificaremos un elemento h3
:
1 |
<ol class="level-2-wrapper"> |
2 |
<li>
|
3 |
... |
4 |
<ol class="level-3-wrapper"> |
5 |
<li>
|
6 |
<h3 class="level-3 rectangle">...</h3> |
7 |
</li>
|
8 |
<li>
|
9 |
<h3 class="level-3 rectangle">...</h3> |
10 |
</li>
|
11 |
</ol>
|
12 |
</li>
|
13 |
<li>
|
14 |
... |
15 |
<ol class="level-3-wrapper"> |
16 |
<li>
|
17 |
<h3 class="level-3 rectangle">...</h3> |
18 |
</li>
|
19 |
<li>
|
20 |
<h3 class="level-3 rectangle">...</h3> |
21 |
</li>
|
22 |
</ol>
|
23 |
</li>
|
24 |
</ol>
|
CSS
De manera similar al nivel anterior, vamos a considerar cada elemento de lista como un contenedor de cuadrículas y lo dividiremos en dos columnas.
De la misma forma, vamos a definir los pseudoelementos ::before
y ::after
de los elementos h2
para crear las conexiones necesarias:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-3-wrapper { |
4 |
position: relative; |
5 |
top: 34px; |
6 |
padding-left: 20px; |
7 |
border-left: 2px solid var(--black); |
8 |
}
|
9 |
|
10 |
.level-3-wrapper::before { |
11 |
display: none; |
12 |
content: ""; |
13 |
position: absolute; |
14 |
top: 0; |
15 |
left: 10px; |
16 |
width: 2px; |
17 |
height: 100%; |
18 |
background: var(--black); |
19 |
}
|
20 |
|
21 |
.level-3-wrapper::after { |
22 |
display: none; |
23 |
content: ""; |
24 |
position: absolute; |
25 |
left: 10px; |
26 |
bottom: 0px; |
27 |
width: calc(100% - 10px); |
28 |
height: 2px; |
29 |
background: var(--black); |
30 |
}
|
31 |
|
32 |
.level-3-wrapper > li { |
33 |
display: grid; |
34 |
grid-column-gap: 20px; |
35 |
grid-template-columns: auto auto; |
36 |
}
|
37 |
|
38 |
.level-3-wrapper > li:last-child { |
39 |
margin-top: 80px; |
40 |
}
|
41 |
|
42 |
.level-2-wrapper > li:last-child .level-3-wrapper { |
43 |
top: -34px; |
44 |
}
|
45 |
|
46 |
.level-3 { |
47 |
background: var(--level-3); |
48 |
}
|
49 |
|
50 |
.level-3::before { |
51 |
content: ""; |
52 |
position: absolute; |
53 |
top: 50%; |
54 |
right: 100%; |
55 |
transform: translateY(-50%); |
56 |
width: 20px; |
57 |
height: 2px; |
58 |
background: var(--black); |
59 |
}
|
60 |
|
61 |
.level-3::after { |
62 |
content: ""; |
63 |
position: absolute; |
64 |
top: 50%; |
65 |
left: 100%; |
66 |
transform: translateY(-50%); |
67 |
width: 20px; |
68 |
height: 2px; |
69 |
background: var(--black); |
70 |
}
|
Nota: si tus nodos de texto se extienden a una línea separada, debes cambiar los siguientes valores codificados de forma fija: top: 34px
y top: -34px
.
Nivel #4
Para el cuarto nivel necesitaremos dieciséis nodos. Estos se distribuirán equitativamente en cuatro listas.
Cada nodo del tercer nivel incluirá una lista:



HTML
Todavía en el interior de la lista inicial, en donde reside el segundo nivel, vamos a definir cuatro listas nuevas. Cada una de ellas contendrá cuatro elementos de lista. Para cada elemento de lista especificaremos un elemento h4
:
1 |
<ol class="level-2-wrapper"> |
2 |
<li>
|
3 |
... |
4 |
<ol class="level-3-wrapper"> |
5 |
<li>
|
6 |
... |
7 |
<ol class="level-4-wrapper"> |
8 |
<li>
|
9 |
<h4 class="level-4 rectangle">...</h4> |
10 |
</li>
|
11 |
... |
12 |
</ol>
|
13 |
</li>
|
14 |
<li>
|
15 |
... |
16 |
<ol class="level-4-wrapper"> |
17 |
<li>
|
18 |
<h4 class="level-4 rectangle">...</h4> |
19 |
</li>
|
20 |
... |
21 |
</ol>
|
22 |
</li>
|
23 |
</ol>
|
24 |
</li>
|
25 |
<li>
|
26 |
... |
27 |
<ol class="level-3-wrapper"> |
28 |
<li>
|
29 |
... |
30 |
<ol class="level-4-wrapper"> |
31 |
<li>
|
32 |
<h4 class="level-4 rectangle">...</h4> |
33 |
</li>
|
34 |
... |
35 |
</ol>
|
36 |
</li>
|
37 |
<li>
|
38 |
... |
39 |
<ol class="level-4-wrapper"> |
40 |
<li>
|
41 |
<h4 class="level-4 rectangle">...</h4> |
42 |
</li>
|
43 |
... |
44 |
</ol>
|
45 |
</li>
|
46 |
</ol>
|
47 |
</li>
|
48 |
</ol>
|
CSS
Una vez más llevaremos a cabo las mismas acciones. En primer lugar, usaremos CSS Grid para crear el diseño de los nodos del cuarto nivel. Luego especificaremos los pseudoelementos necesarios de los elementos destino para establecer las conexiones:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-4-wrapper { |
4 |
position: relative; |
5 |
top: 34px; |
6 |
display: grid; |
7 |
grid-template-columns: repeat(4, 1fr); |
8 |
grid-column-gap: 20px; |
9 |
padding-left: 20px; |
10 |
}
|
11 |
|
12 |
.level-4-wrapper::before { |
13 |
content: ""; |
14 |
position: absolute; |
15 |
top: 0; |
16 |
left: 0; |
17 |
width: 2px; |
18 |
height: 50%; |
19 |
background: var(--black); |
20 |
}
|
21 |
|
22 |
.level-3-wrapper > li:last-child .level-4-wrapper { |
23 |
top: -34px; |
24 |
}
|
25 |
|
26 |
.level-3-wrapper > li:last-child .level-4-wrapper::before { |
27 |
top: auto; |
28 |
bottom: 0; |
29 |
}
|
30 |
|
31 |
.level-4 { |
32 |
background: var(--level-4); |
33 |
}
|
34 |
|
35 |
.level-4::before { |
36 |
content: ""; |
37 |
position: absolute; |
38 |
top: 50%; |
39 |
right: 100%; |
40 |
transform: translateY(-50%); |
41 |
width: 20px; |
42 |
height: 2px; |
43 |
background: var(--black); |
44 |
}
|
Nota: si tus nodos de texto se extienden a una línea separada, debes cambiar los valores top: 34px
y top: -34px
, que están codificados de forma fija.
4. Agrega responsividad
Como ya has adivinado, para nuestros estilos hemos seguido un enfoque de escritorio. En términos generales, yo prefiero construir teniendo en cuenta las pantallas grandes en primer lugar, y luego paso a las pantallas más pequeñas y menos complicadas.
En las pantallas de entre 1301px y 1650px, el gráfico seguirá mostrándose en modo horizontal, pero sus nodos tendrán un ancho menor (200px en lugar de 150px).
En pantallas de hasta 1300px de ancho, todos los nodos del gráfico se mostrarán de manera vertical, de esta forma:



A diferencia del diseño horizontal, aquí el ancho y la tipografía de los nodos de texto difieren dependiendo de sus niveles. Mientras más alto sea el nivel, más anchos serán los nodos y más grande será la tipografía.
Por supuesto, no dudes en cambiar estos puntos de interrupción dependiendo de tu contenido.
Vale la pena señalar que este diseño es ligeramente diferente en comparación con el diseño responsivo del gráfico vertical:



En caso de que te lo preguntes, no hay un propósito específico detrás de esta nueva implementación. Solamente quería mostrarte dos diseños responsivos diferentes. No dudes en utilizar cualquiera de estos en tus páginas.
Estos son los estilos responsivos:
1 |
@media screen and (max-width: 1650px) { |
2 |
.rectangle { |
3 |
width: 150px; |
4 |
}
|
5 |
}
|
6 |
|
7 |
@media screen and (max-width: 1300px) { |
8 |
body { |
9 |
font-size: 16px; |
10 |
}
|
11 |
|
12 |
h1, |
13 |
h2, |
14 |
h3, |
15 |
h4 { |
16 |
font-size: revert; |
17 |
}
|
18 |
|
19 |
.rectangle { |
20 |
padding: 20px 10px; |
21 |
width: auto; |
22 |
}
|
23 |
|
24 |
.container { |
25 |
display: block; |
26 |
}
|
27 |
|
28 |
.level-1 { |
29 |
margin-bottom: 20px; |
30 |
}
|
31 |
|
32 |
.level-1::before, |
33 |
.level-2::after, |
34 |
.level-3::after { |
35 |
display: none; |
36 |
}
|
37 |
|
38 |
.level-2-wrapper::before, |
39 |
.level-2-wrapper::after, |
40 |
.level-3-wrapper::before, |
41 |
.level-3-wrapper::after, |
42 |
.level-2-wrapper > li, |
43 |
.level-3-wrapper > li { |
44 |
display: block; |
45 |
}
|
46 |
|
47 |
.level-2-wrapper { |
48 |
padding-left: 30px; |
49 |
border-left: none; |
50 |
}
|
51 |
|
52 |
.level-2-wrapper > li:last-child { |
53 |
margin-top: 50px; |
54 |
}
|
55 |
|
56 |
.level-2-wrapper > li:last-child .level-3-wrapper, |
57 |
.level-3-wrapper > li:last-child .level-4-wrapper, |
58 |
.level-3-wrapper, |
59 |
.level-4-wrapper { |
60 |
top: 0; |
61 |
}
|
62 |
|
63 |
.level-3-wrapper { |
64 |
padding: 20px 0 20px 30px; |
65 |
border-left: none; |
66 |
}
|
67 |
|
68 |
.level-3-wrapper > li:last-child { |
69 |
margin-top: 50px; |
70 |
}
|
71 |
|
72 |
.level-4-wrapper { |
73 |
padding: 20px 0 0 30px; |
74 |
grid-template-columns: repeat(2, 1fr); |
75 |
}
|
76 |
|
77 |
.level-4-wrapper > li:first-child { |
78 |
margin-bottom: 20px; |
79 |
}
|
80 |
|
81 |
.level-4-wrapper::before { |
82 |
left: 10px; |
83 |
height: 100%; |
84 |
}
|
85 |
}
|
La palabra clave revert
De los estilos anteriores, presta especial atención a la nueva palabra clave revert
de CSS que aplicamos a los encabezados. Eso nos ayudará a restaurar sus estilos de navegador predeterminados. Afortunadamente, su compatibilidad con los navegadores crece día a día.
Conclusión
¡Eso es todo por hoy, amigos! En este punto, ya cuentas con todo lo que necesitas para crear tus propios organigramas usando solamente CSS.
Por supuesto, en un escenario real quizá prefieras usar una potente biblioteca de JavaScript, como Highcharts.js, para crear un gráfico de este tipo. Sin embargo, extender los límites de CSS sin ignorar sus limitaciones siempre es una excelente manera de aprender y expandir tus conocimientos. Además, con la combinación de herramientas y funciones como CSS Grid y pseudoelementos, puedes lograr cosas verdaderamente interesantes.
Recordemos lo que hemos creado:
Sigue adelante, bifurca la demostración y ponte manos a la obra. No tengas miedo de destruirla y experimentar con ella. Puedes lograr diferentes variaciones sin poner demasiado esfuerzo. Por ejemplo, esta es otra alternativa rápida para las pantallas de escritorio:



Por último, pero no por eso menos importante, aquí en Tuts+ existen muchos tutoriales detallados que muestran cómo implementar diferentes tipos de gráficos en CSS (en algunas ocasiones con JavaScript). Asegúrate de echarles un vistazo y, como siempre, ¡muchas gracias por leer!
- Gráficos en CSSCómo crear un diagrama de Gantt simple con CSS y JavaScriptGeorge Martsoukos
- CSSCómo crear un gráfico de termómetro animado en CSSGeorge Martsoukos
- CSSCrea un portafolio estático con un avanzado gráfico de barras en CSSGeorge Martsoukos
- CSSGráficos en CSS: cómo crear un organigramaGeorge Martsoukos
- CSSCómo construir una página de portafolio estática con CSS y JavaScriptGeorge Martsoukos
- CSSCómo construir un gráfico de anillo semicircular con CSSGeorge Martsoukos