[[!meta title="Pure CSS Bar Graphs with Graceful Mobile Fallbacks"]]
[[!meta date="2020-12-08"]]
I recently published a new open source project, Flexbox Bar Graphs, and wanted to share a simple breakdown of how it was built. It isn't anything mind-blowing, but I like the idea of placing bar graphs in a web page with *zero* Javascript.
So in the end, this is what our bar graphs will look like on desktop:
And this is how it will look on smaller devices:
Let's get into the details!
## The HTML
The main "secret" of this project is that our graphs are constructed out of HTML *tables*. Now before you freak out - this is perfectly fine and works in our favor quite well.
1. If the user has JS disabled --> they will still see our graphs
2. If the user has CSS disabled --> they will see a standard data table set
All bases are covered!
Web Performance Results
Test Performed
Before
After
Difference
Initial Load Time
4.7
2.7
2
Nothing crazy is happening here - just your standard HTML table structure. The one main thing to notice is the `--data-set` CSS variable placed inline on each data point. This will be important for our CSS to configure the individual bar graphs properly.
## The CSS
This might look overwhelming if I just dumped the whole CSS file in one big code block, so instead I'm going to break them down into two parts:
1. Baseline styling (mobile)
2. Desktop styling
### Baseline
Here we target just our table elements with the `data-id` of `flexbox-bar-graph`. This allows us to avoid worrying about adding classes or IDs and also avoids conflicts with other non-graph styled tables in our projects.
The base `:root` element holds all of our bar graph colors. Change these as you see fit!
/* Bar Graph color variables */
:root {
--bar-color-1: #357EC7;
--bar-color-2: #E42217;
--bar-color-3: #4CC417;
--bar-color-4: #7D0541;
--bar-color-5: #FFD801;
}
[data-id="flexbox-bar-graph"] {
border-collapse: collapse;
margin: 4rem 0 6rem;
width: 100%;
}
[data-id="flexbox-bar-graph"] caption {
text-align: left;
}
[data-id="flexbox-bar-graph"] thead th {
text-align: right;
}
[data-id="flexbox-bar-graph"] thead th:nth-child(1),
[data-id="flexbox-bar-graph"] tbody th {
text-align: left;
}
[data-id="flexbox-bar-graph"] tbody th {
font-weight: normal;
font-style: italic;
}
[data-id="flexbox-bar-graph"] tbody td {
text-align: right;
}
[data-id="flexbox-bar-graph"] tbody td p {
margin: 0;
}
### Desktop
Now we set your "visual" bar graphs to show at a set width (in this example it is 1000px and above). That way the "default" styling can target the mobile device screen sizes.
- The `thead tr th:nth-child(x):before` elements create the square "legends" beside each individual data point heading
- The `tbody tr td:nth-of-type(x) span` elements are the bars themselves
@media(min-width: 1000px) {
[data-id="flexbox-bar-graph"] {
background: transparent;
display: block;
min-height: 400px;
padding: 0;
position: relative;
width: 100%;
}
[data-id="flexbox-bar-graph"] caption {
display: block;
font-size: 2rem;
text-align: center;
width: 100%;
}
[data-id="flexbox-bar-graph"] thead {
display: block;
margin: 2rem 0 3rem;
width: 100%;
}
[data-id="flexbox-bar-graph"] thead tr {
border-bottom: 1px solid lightgrey;
display: flex;
justify-content: center;
padding-bottom: 1rem;
}
[data-id="flexbox-bar-graph"] thead tr th {
display: inline-block;
margin: 0;
padding: 0;
position: relative;
text-align: right;
}
[data-id="flexbox-bar-graph"] thead tr th:before {
content:'';
display: inline-block;
height: 10px;
margin: 0 0.5rem 0 2rem;
position: relative;
width: 10px;
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(1),
[data-id="flexbox-bar-graph"] thead tr th:nth-child(1):before {
display: none;
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(2):before {
background: var(--bar-color-1);
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(3):before {
background: var(--bar-color-2);
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(4):before {
background: var(--bar-color-3);
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(5):before {
background: var(--bar-color-4);
}
[data-id="flexbox-bar-graph"] thead tr th:nth-child(6):before {
background: var(--bar-color-5);
}
[data-id="flexbox-bar-graph"] tbody {
display: flex;
justify-content: space-between;
min-height: 300px;
width: 100%;
}
[data-id="flexbox-bar-graph"] tbody tr {
display: flex;
flex-direction: column-reverse;
flex-wrap: wrap;
justify-content: flex-end;
padding: 0 50px;
position: relative;
width: 100%;
}
[data-id="flexbox-bar-graph"] tbody tr th {
font-size: 90%;
position: absolute;
text-align: center;
top: 100%;
width: calc(100% - 100px);
}
[data-id="flexbox-bar-graph"] tbody tr td {
align-items: center;
display: flex;
flex-direction: column;
height: 95%;
justify-content: flex-end;
}
[data-id="flexbox-bar-graph"] tbody tr td span {
display: block;
height: calc(var(--data-set) * 100%);
width: 20px;
}
[data-id="flexbox-bar-graph"] tbody tr td:nth-of-type(1) span {
background: var(--bar-color-1);
}
[data-id="flexbox-bar-graph"] tbody tr td:nth-of-type(2) span {
background: var(--bar-color-2);
}
[data-id="flexbox-bar-graph"] tbody tr td:nth-of-type(3) span {
background: var(--bar-color-3);
}
[data-id="flexbox-bar-graph"] tbody tr td:nth-of-type(4) span {
background: var(--bar-color-4);
}
[data-id="flexbox-bar-graph"] tbody tr td:nth-of-type(5) span {
background: var(--bar-color-5);
}
[data-id="flexbox-bar-graph"] tbody tr td p {
font-size: 90%;
margin: 0;
text-align: center;
}
}
## Bonus Styling
In the Flexbox Bar Graph repo, I've also included the ability to display these bar graphs horizontally, like so:
The change in CSS is actually quite simple to pull this off - you just need to include the `data-layout` attribute on the table itself.
[data-layout="horizontal"] tbody {
min-height: auto;
}
[data-layout="horizontal"] tbody tr {
flex-direction: column;
padding: 0 40px;
}
[data-layout="horizontal"] tbody tr th {
width: calc(100% - 80px);
}
[data-layout="horizontal"] tbody tr th {
text-align: left;
top: calc(100% + 20px);
}
[data-layout="horizontal"] tbody tr td {
flex-direction: row;
height: auto;
justify-content: start;
margin: 10px 0;
}
[data-layout="horizontal"] tbody tr td span {
height: 20px;
width: calc(var(--data-set) * 100%);
}
[data-layout="horizontal"] tbody tr td p {
margin-left: 10px;
}
## That's All Folks!
That just about sums things up. Feel free to check out the Github repo itself, open any issues you find or fork it for your own!
- Github: https://github.com/bradleytaunt/flexbox-bar-graphs
- Live Demo: https://flexbox-bar-graphs.netlify.app/