In previous tutorials we’ve learned how to create different types of charts including bar charts, thermometer charts, and donut charts. Today we’ll continue this journey by building a CSS-only organizational chart.
Ready to test your CSS skills?
Here’s the CSS chart we’ll be creating:
It consists of four levels and describes the hierarchical structure of a company.
To find out what is an organizational chart, let’s borrow Wikipedia’s definition:
“An organizational chart , also called organigram or organogram, is a diagram that shows the structure of an organization and the relationships and relative ranks of its parts and positions/jobs. The term is also used for similar diagrams, for example ones showing the different elements of a field of knowledge or a group of languages.”
Here’s an example:
This type of chart is commonly used for presenting the relationships between the people or departments of a company. On a corporate website, you will probably find it on the “About Us” or “Company” page.
You’ll also see organizational charts used for family trees (check out the British Royal Family tree and line of succession on the BBC’s website). They’re ideally suited for illustrating hierarchy.
Our chart in CSS will live inside a container:
Before going through its levels, we’ll set up a few reset rules and helper classes:
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-family: "Inter", sans-serif; |
23 |
}
|
24 |
|
25 |
.container { |
26 |
max-width: 1000px; |
27 |
padding: 0 10px; |
28 |
margin: 0 auto; |
29 |
}
|
30 |
|
31 |
.rectangle { |
32 |
position: relative; |
33 |
padding: 20px; |
34 |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15); |
35 |
}
|
Notice the rectangle
class. We’ll append this to every node/element of our chart.
Note: for simplicity, I haven’t optimized the CSS. This will help you get a better understanding of the styles of each level.
At this point, we’re ready to specify the chart levels; as we discussed earlier, here we’ll have four of them:
Each level will represent a role in a company starting from the highest-ranking one. Keep this in mind as you’re generating a hierarchy chart design.
The first level will only include a single node:
To describe it, we’ll use an h1
tag as it’s the most important part of our chart:
We’ll use its ::before
pseudo-element to create a relationship between the first and second levels:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-1 { |
4 |
width: 50%; |
5 |
margin: 0 auto 40px; |
6 |
background: var(--level-1); |
7 |
}
|
8 |
|
9 |
.level-1::before { |
10 |
content: ""; |
11 |
position: absolute; |
12 |
top: 100%; |
13 |
left: 50%; |
14 |
transform: translateX(-50%); |
15 |
width: 2px; |
16 |
height: 20px; |
17 |
background: var(--black); |
18 |
}
|
The second level will consist of two nodes:
As we’ll see in a moment, each node will include other child nodes.
These child nodes will represent lower levels of the managerial hierarchy.
To describe it, we’ll use an ordered list with two list items. Each list item will contain an h2
element:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
Thanks to CSS Grid, we’ll create the layout for this level.
Next, we’ll use the ::before
pseudo-element of specific elements for creating the associations between the nodes of this level and the adjacent levels:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-2-wrapper { |
4 |
position: relative; |
5 |
display: grid; |
6 |
grid-template-columns: repeat(2, 1fr); |
7 |
}
|
8 |
|
9 |
.level-2-wrapper::before { |
10 |
content: ""; |
11 |
position: absolute; |
12 |
top: -20px; |
13 |
left: 25%; |
14 |
width: 50%; |
15 |
height: 2px; |
16 |
background: var(--black); |
17 |
}
|
18 |
|
19 |
.level-2-wrapper::after { |
20 |
display: none; |
21 |
content: ""; |
22 |
position: absolute; |
23 |
left: -20px; |
24 |
bottom: -20px; |
25 |
width: calc(100% + 20px); |
26 |
height: 2px; |
27 |
background: var(--black); |
28 |
}
|
29 |
|
30 |
.level-2-wrapper li { |
31 |
position: relative; |
32 |
}
|
33 |
|
34 |
.level-2-wrapper > li::before { |
35 |
content: ""; |
36 |
position: absolute; |
37 |
bottom: 100%; |
38 |
left: 50%; |
39 |
transform: translateX(-50%); |
40 |
width: 2px; |
41 |
height: 20px; |
42 |
background: var(--black); |
43 |
}
|
44 |
|
45 |
.level-2 { |
46 |
width: 70%; |
47 |
margin: 0 auto 40px; |
48 |
background: var(--level-2); |
49 |
}
|
50 |
|
51 |
.level-2::before { |
52 |
content: ""; |
53 |
position: absolute; |
54 |
top: 100%; |
55 |
left: 50%; |
56 |
transform: translateX(-50%); |
57 |
width: 2px; |
58 |
height: 20px; |
59 |
background: var(--black); |
60 |
}
|
61 |
|
62 |
.level-2::after { |
63 |
display: none; |
64 |
content: ""; |
65 |
position: absolute; |
66 |
top: 50%; |
67 |
left: 0%; |
68 |
transform: translate(-100%, -50%); |
69 |
width: 20px; |
70 |
height: 2px; |
71 |
background: var(--black); |
72 |
}
|
Notice that we also define the ::after
pseudo-element of the second-level nodes. This will appear only on small screens.
The third level will include four nodes.
We’ll associate the first two nodes with the first node of the second level, while the last two with its second node:
Still, inside the initial list where the second level lives, we’ll define two new lists. Each one of them will contain two list items. For each item will specify an h3
element:
1 |
|
2 |
|
3 |
... |
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
... |
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
Thanks again to CSS Grid, we’ll position the nodes.
In the same way, we’ll set the ::before
pseudo-element of specific elements for creating the required connections:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-3-wrapper { |
4 |
position: relative; |
5 |
display: grid; |
6 |
grid-template-columns: repeat(2, 1fr); |
7 |
grid-column-gap: 20px; |
8 |
width: 90%; |
9 |
margin: 0 auto; |
10 |
}
|
11 |
|
12 |
.level-3-wrapper::before { |
13 |
content: ""; |
14 |
position: absolute; |
15 |
top: -20px; |
16 |
left: calc(25% - 5px); |
17 |
width: calc(50% + 10px); |
18 |
height: 2px; |
19 |
background: var(--black); |
20 |
}
|
21 |
|
22 |
.level-3-wrapper > li::before { |
23 |
content: ""; |
24 |
position: absolute; |
25 |
top: 0; |
26 |
left: 50%; |
27 |
transform: translate(-50%, -100%); |
28 |
width: 2px; |
29 |
height: 20px; |
30 |
background: var(--black); |
31 |
}
|
32 |
|
33 |
.level-3 { |
34 |
margin-bottom: 20px; |
35 |
background: var(--level-3); |
36 |
}
|
We’ll need sixteen nodes for the fourth level. These will equally be distributed into four lists.
Each third-level node will include one list:
Still, inside the initial list where the second level lives, we’ll define four new lists. Each one of them will contain four list items. For each item will specify an h4
element:
1 |
|
2 |
|
3 |
... |
4 |
|
5 |
|
6 |
... |
7 |
|
8 |
|
9 |
|
10 |
|
11 |
... |
12 |
|
13 |
|
14 |
|
15 |
... |
16 |
|
17 |
|
18 |
|
19 |
|
20 |
... |
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
... |
27 |
|
28 |
|
29 |
... |
30 |
|
31 |
|
32 |
|
33 |
|
34 |
... |
35 |
|
36 |
|
37 |
|
38 |
... |
39 |
|
40 |
|
41 |
|
42 |
|
43 |
... |
44 |
|
45 |
|
46 |
|
47 |
|
48 |
|
Once more, we’ll set out the ::before
pseudo-element of specific elements for associating the fourth-level nodes with their parents:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
.level-4-wrapper { |
4 |
position: relative; |
5 |
width: 80%; |
6 |
margin-left: auto; |
7 |
}
|
8 |
|
9 |
.level-4-wrapper::before { |
10 |
content: ""; |
11 |
position: absolute; |
12 |
top: -20px; |
13 |
left: -20px; |
14 |
width: 2px; |
15 |
height: calc(100% + 20px); |
16 |
background: var(--black); |
17 |
}
|
18 |
|
19 |
.level-4-wrapper li + li { |
20 |
margin-top: 20px; |
21 |
}
|
22 |
|
23 |
.level-4 { |
24 |
font-weight: normal; |
25 |
background: var(--level-4); |
26 |
}
|
27 |
|
28 |
.level-4::before { |
29 |
content: ""; |
30 |
position: absolute; |
31 |
top: 50%; |
32 |
left: 0%; |
33 |
transform: translate(-100%, -50%); |
34 |
width: 20px; |
35 |
height: 2px; |
36 |
background: var(--black); |
37 |
}
|
Making an organizational chart in CSS responsive is tricky. I remember myself having to reconstruct the markup one or two times until coming up with this version. So, if you plan to create such a chart in CSS, I recommend you follow a mobile-first approach.
With all this in mind, here’s its mobile layout:
To accomplish this behavior and generate a hierarchy chart design that’s responsive, we have to modify some styles:
1 |
@media screen and (max-width: 700px) { |
2 |
.rectangle { |
3 |
padding: 20px 10px; |
4 |
}
|
5 |
|
6 |
.level-1, |
7 |
.level-2 { |
8 |
width: 100%; |
9 |
}
|
10 |
|
11 |
.level-1 { |
12 |
margin-bottom: 20px; |
13 |
}
|
14 |
|
15 |
.level-1::before, |
16 |
.level-2-wrapper > li::before { |
17 |
display: none; |
18 |
}
|
19 |
|
20 |
.level-2-wrapper, |
21 |
.level-2-wrapper::after, |
22 |
.level-2::after { |
23 |
display: block; |
24 |
}
|
25 |
|
26 |
.level-2-wrapper { |
27 |
width: 90%; |
28 |
margin-left: 10%; |
29 |
}
|
30 |
|
31 |
.level-2-wrapper::before { |
32 |
left: -20px; |
33 |
width: 2px; |
34 |
height: calc(100% + 40px); |
35 |
}
|
36 |
|
37 |
.level-2-wrapper > li:not(:first-child) { |
38 |
margin-top: 50px; |
39 |
}
|
40 |
}
|
Congrats, folks! Without writing a single line of JavaScript, we managed to build a fully functional organizational chart.
Let’s remind ourselves of what we did while learning to generate a hierarchy chart design:
Of course, keep in mind that our chart has a specific structure. Depending on your needs, you might want to enrich its content or modify its layout to generate a hierarchy chart design suited to you. If you need something more advanced or dynamic, have a look at some JavaScript libraries like Highcharts.js.
Have you ever created a CSS chart? If so, please share your experience with us!
If you still need some inspiration, don’t forget to check at my other charts in the Tuts+ archive or do a quick search on CodePen.
As always, thanks a lot for reading!