Bar charts
This type of graph is used to represent one-dimensional data (in our example, a timeline). It's based on CSS grids and custom properties, a technique inspired by an article by Miriam Suzanne on CSS Tricks with a slight enhancement to improve accessibility. Here's how to use it:
-
On the table itself, the --scale custom property is used to define the maximum value
for the graph,
in order to determine its scale. Concretely, a grid will be generated with:
- the first column dedicated to header cells
<th>
arbitrarily set to12.5em
, though being configurable through --offset; - then the CSS
repeat()
function creates a column per scale unit — in the example, 3000 columns; -
and finally the last column measuring
10ch
, literally meaning enough space for ten "0" characters have a look to CSS units rudiments, documented in Every Layout by Andy Bell and Heydon Pickering.
- the first column dedicated to header cells
-
On each cell
<td>
, a --value custom property allows to place it on the grid, applied togrid-column-end
. Moreover, thanks to clever calculations based on this value, the background gradient is sized and positioned to reflect the proportion represented by this value on the given scale (from green for almost nothing to red for almost everything). -
In each cell, the content must include the value and its unit in a
<span>
element, possibly tagged with<abbr>
(andaria-label
to complementtitle
) if a title can explicit the unit. This value is pushed to the right of the grid, and its text serves as a mask for the background gradient — thanks to S. Shaw's trick to applybackground-clip: text
as a progressive enhancement — allowing it to be the corresponding color at the end of the gradient for the given position.
HTML
<table class="chaarts bar" style="--scale: 3000">
<caption id="caption-1">[…]</caption>
<thead class="sr-only">
<tr>
<td></td>
<th scope="col">[…]</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">[…]</th>
<td style="--value: 4">
<span>[…]</span>
</td>
</tr>
<tr>[…]</tr>
</tbody>
</table>
css
.chaarts.bar {
--offset: 12.5em;
}
.chaarts.bar caption {
text-align: initial;
text-indent: calc(var(--offset) + 1rem);
}
.chaarts.bar tr {
display: grid;
grid-auto-rows: 1fr;
grid-row-gap: 0.5rem;
grid-template-columns: minmax(min-content, var(--offset)) repeat(var(--scale, 100), minmax(0, 1fr)) 10ch;
transition: opacity 0.2s var(--move);
}
.chaarts.bar tr:nth-of-type(1n + 1) {
--background: var(--checkers);
}
.chaarts.bar tr:nth-of-type(2n + 2) {
--background: var(--hexagons);
}
.chaarts.bar tr:nth-of-type(3n + 3) {
--background: var(--triangles);
}
.chaarts.bar tr:nth-of-type(4n + 4) {
--background: var(--zig);
}
.chaarts.bar tr:nth-of-type(5n + 5) {
--background: var(--stripes);
}
.chaarts.bar tr:nth-of-type(6n + 6) {
--background: var(--dots);
}
.chaarts.bar th {
grid-column: 1/1;
margin: 0.5rem 0 0;
padding: 0 1rem 0 0;
text-align: end;
}
.chaarts.bar td {
--size: calc(var(--scale, 100) * 100%);
--position: calc(var(--value, 0) / var(--scale, 100) * 100%);
background: linear-gradient(to right, var(--chaarts-green), var(--chaarts-gray), var(--chaarts-blue), var(--chaarts-purple), var(--chaarts-red)) var(--position) 0% / var(--size) 100%, var(--background) center/contain;
background-blend-mode: hard-light;
grid-column: 2 / max(2, var(--value, 0));
margin: 0.5rem 0 0;
position: relative;
}
.chaarts.bar span {
background: inherit;
background-clip: text;
color: transparent;
font-weight: bold;
inset-inline-start: 100%;
line-height: 1.5;
padding-inline-start: 0.5ch;
position: absolute;
}
.chaarts.bar:hover tr {
opacity: 0.5;
}
.chaarts.bar:hover tr:hover {
opacity: 1;
}
@media (prefers-contrast: more) {
.chaarts.bar td {
background: var(--chaarts-blue);
}
.chaarts.bar span {
background: var(--background-lighter);
background-clip: unset;
color: var(--chaarts-blue);
}
}
Waterfall
The principle is the same for this variant, except for one detail: we also manage
the starting point for each measurement
— which is, very simply, the value of the previous point…
However, all the values must be passed as variables on the parent <table>
.
HTML
<table class="chaarts bar waterfall"
style="--scale: 3000; --1: 4; --2: 96; --3: 102; --4: 129; --5: 188; --6: 194; --7: 326; --8: 836; --9: 836; --10: 2561; --11: 2980;">
</table>
css
.chaarts.bar.waterfall tr:nth-of-type(1) td {
grid-column: var(--0, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(2) td {
grid-column: var(--1, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(3) td {
grid-column: var(--2, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(4) td {
grid-column: var(--3, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(5) td {
grid-column: var(--4, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(6) td {
grid-column: var(--5, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(7) td {
grid-column: var(--6, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(8) td {
grid-column: var(--7, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(9) td {
grid-column: var(--8, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(10) td {
grid-column: var(--9, 2) / var(--value, 2);
}
.chaarts.bar.waterfall tr:nth-of-type(11) td {
grid-column: var(--10, 2) / var(--value, 2);
}