aboutsummaryrefslogtreecommitdiff
path: root/posts/css-slope-graphs.md
diff options
context:
space:
mode:
authorBradley Taunt <bt@btxx.org>2024-06-06 08:05:12 -0400
committerBradley Taunt <bt@btxx.org>2024-06-06 08:05:12 -0400
commit6b742c459266b18e2b375b35205ce8a6c02f0452 (patch)
treeb16fbb9a045e33dd6c97eb5ab72e6ff4d9237ea3 /posts/css-slope-graphs.md
Initial commit
Diffstat (limited to 'posts/css-slope-graphs.md')
-rw-r--r--posts/css-slope-graphs.md229
1 files changed, 229 insertions, 0 deletions
diff --git a/posts/css-slope-graphs.md b/posts/css-slope-graphs.md
new file mode 100644
index 0000000..8c3ce3a
--- /dev/null
+++ b/posts/css-slope-graphs.md
@@ -0,0 +1,229 @@
+# CSS Slope Graphs
+
+2021-06-07
+
+*I am a huge sucker for simplistic and beautifully designed visual data on the web*. Most data tends to be graphed via line or bar systems - which is fine - but I think slope graphs are highly underrated. Let's change that, shall we?
+
+## The Demo
+
+I'm basing this demo off the design patterns found in [Edward Tufte's visualization work](https://www.edwardtufte.com/tufte/books_vdqi), specifically his slope graph designs:
+
+[Live CodePen Example](https://codepen.io/bradleytaunt/pen/jOBzXMe)
+
+## The HTML
+
+For this concept we will actually be building this graph out of `tables` - crazy, right? The greatest benefit of rendering all the data inside of a `table` element is the ability to easily support smaller screens and mobile devices. Larger viewports will get to see the pretty slope graph, while those below a certain threshold will view a simple table.
+
+(But more on that in the CSS section)
+
+
+ <p>Sales of the leading frozen pizza brands of the United States from 2011 to 2017 (in million US dollars) <br><em>Source: Statisa 2018</em></p>
+ <table>
+ <thead>
+ <tr>
+ <th>Pizza Brand</th>
+ <th>2011</th>
+ <th>2017</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td data-set="677.0">DiGiorno</td>
+ <td><span>677.0</span></td>
+ <td data-name="DiGiorno">1014.6</td>
+ </tr>
+ <tr>
+ <td data-set="294.8">Private Label</td>
+ <td><span>294.8</span></td>
+ <td data-name="Private Label">524.8</td>
+ </tr>
+ <tr>
+ <td data-set="286.1">Red Baron</td>
+ <td><span>286.1</span></td>
+ <td data-name="Red Baron">572.3</td>
+ </tr>
+ <tr>
+ <td data-set="257.9">Tombstone</td>
+ <td><span>257.9</span></td>
+ <td data-name="Tombstone">270.6</td>
+ </tr>
+ <tr>
+ <td data-set="164.5">Totino's Party Pizza</td>
+ <td><span>164.5</span></td>
+ <td data-name="Totino's Party Pizza">347.2</td>
+ </tr>
+ </tbody>
+ </table>
+
+
+As you can see, nothing too fancy is happpening here. Pay close attention to the `data-set` and `data-name` variables though - those will be important for the CSS portion of this design, mainly the rendering of the line elements.
+
+
+
+## The CSS
+
+To avoid overwhelming your brain all-at-once, let's break the CSS down into bite-sized chunks, starting with the base styling:
+
+
+ @import url('https://opentype.netlify.com/et-book/index.css');
+ * {
+ box-sizing: border-box;
+ }
+
+ html {
+ height: 100%;
+ }
+
+ body {
+ background: #fffff8;
+ font-family: "et-book", serif;
+ height: 100%;
+ margin: 0 auto;
+ max-width: 800px;
+ padding: 0 0.5rem;
+ }
+
+ p {
+ font-size: 18px;
+ margin: 4rem 0 6rem;
+ }
+
+ table {
+ border-collapse: collapse;
+ text-align: left;
+ width: 100%;
+ }
+
+
+Pretty basic stuff.
+
+Now we need to design how our slope graph will look on larger screens / desktops. For this instance, we will target these larger devices with a `min-width` media query of `800px`. The rest of the CSS might look a little confusing but I assure you it is quite simple.
+
+1. On larger devices we hide the first `thead tr th` element with `display: none`
+2. The first and second `td` elements inside each `tbody` row need to be set as `position: absolute` to avoid duplicate content
+3. The inner `span` that we include in our HTML inside the second `tbody tr td` also needs to be `display: none`
+4. Remember that `data-set` variable? We now use that for our `:before` pseudo element for `table tbody tr td:nth-of-type(1)`
+5. Remember that `data-name` variable? We now use that for our `:before` pseudo element for `table tbody tr td:nth-of-type(3)`
+6. After that, you can see the simple customization we include to render the angle / position of the slope lines and the corresponding labels
+
+
+ @media(min-width:800px) {
+ table {
+ display: block;
+ position: relative;
+ margin-bottom: 25rem;
+ }
+
+ table thead th {
+ border-bottom: 1px solid lightgrey;
+ font-size: 24px;
+ position: absolute;
+ top: -50px;
+ width: 45%;
+ }
+ table thead th:nth-child(1){ display: none; }
+ table thead th:nth-child(2){ left: 0; }
+ table thead th:nth-child(3){ right: 0; text-align: right; }
+
+ table tbody tr td:nth-of-type(1),
+ table tbody tr td:nth-of-type(2) { position: absolute;}
+
+ table tbody tr td:nth-of-type(2) span { display: none; }
+ table tbody tr td:nth-of-type(1):before {
+ content: attr(data-set);
+ margin-right: 10px;
+ position: relative;
+ }
+
+ table tbody tr td:nth-of-type(2) { padding-left: 10px; }
+
+ table tbody tr td:nth-of-type(3) {
+ position: absolute;
+ right: 0;
+ }
+ table tbody tr td:nth-of-type(3):before {
+ content: attr(data-name);
+ margin-right: 10px;
+ position: relative;
+ }
+
+ /* Custom individual slopes -- Left */
+ tbody tr:nth-child(1) td:nth-child(1),
+ tbody tr:nth-child(1) td:nth-child(2) { top: 60px; }
+ tbody tr:nth-child(2) td:nth-child(1),
+ tbody tr:nth-child(2) td:nth-child(2) { top: 140px; }
+ tbody tr:nth-child(3) td:nth-child(1),
+ tbody tr:nth-child(3) td:nth-child(2) { top: 165px; }
+ tbody tr:nth-child(4) td:nth-child(1),
+ tbody tr:nth-child(4) td:nth-child(2) { top: 220px; }
+ tbody tr:nth-child(5) td:nth-child(1),
+ tbody tr:nth-child(5) td:nth-child(2) { top: 270px; }
+
+ /* Custom individual slopes -- Right */
+ [data-name="DiGiorno"] { top: 0; }
+ [data-name="Red Baron"] { top: 65px; }
+ [data-name="Private Label"] { top: 100px; }
+ [data-name="Tombstone"] { top: 180px; }
+ [data-name="Totino's Party Pizza"] { top: 150px; }
+
+ /* The custom visual lines */
+ tbody tr:after {
+ background: black;
+ content: '';
+ height: 1px;
+ left: 14.5%;
+ position: absolute;
+ width: 70%;
+ }
+ tbody tr:nth-child(1):after {
+ top: 40px;
+ transform: rotate(-6deg);
+ }
+ tbody tr:nth-child(2):after {
+ left: 17.5%;
+ top: 130px;
+ transform: rotate(-4deg);
+ width: 65%;
+ }
+ tbody tr:nth-child(3):after {
+ left: 15%;
+ top: 125px;
+ transform: rotate(-10.25deg);
+ width: 70%;
+ }
+ tbody tr:nth-child(4):after {
+ left: 16%;
+ top: 210px;
+ transform: rotate(-4deg);
+ width: 68%;
+ }
+ tbody tr:nth-child(5):after {
+ left: 22%;
+ top: 222px;
+ transform: rotate(-16deg);
+ width: 56%;
+ }
+ }
+
+
+All that's left are some minor styles to make everything look nice on mobile:
+
+
+ @media(max-width:800px) {
+ p {
+ margin: 2rem 0;
+ }
+ table td, table th {
+ border-bottom: 1px solid grey;
+ padding: 10px;
+ }
+ table td:last-of-type, table th:last-of-type {
+ text-align: right;
+ }
+ }
+
+
+## Not the most practical
+This slope graph concept is far from perfect for use in real-world situations. The fact that you need to manually render each point of data yourself makes this implementation quite annoying for more in-depth projects.
+
+But it was fun to mess around with and create, so who cares!