Writing
4 min
front-end · tutorials · writing

How to Create a Bar Chart Archive Widget In WordPress

December 3, 2012 · 4 min

Each time I redesign my blog, I try to implement some cool, visually interesting feature. This time around, I decided to build a widget to display post archives, styled to look like a vertical bar chart:

Making this widget turned out to be a bit tricky, but fun. In this post, I’ll describe how to do it.

1. Lay Out the Widget

We’ll start with the HTML. I chose to use two lists: one for the boxes and one for the labels. I should note that this is not the most semantically accurate way of doing it (because the two lists contain essentially the same information). That said, I found it easier to style than the alternative.

Here’s the HTML:

<aside class="archives">
<h3>archives</h3>
<ul class="archive-boxes">
<li class="archive-item-container"><a class="archive-box width-5" href="...">5</a></li>
....
<li class="archive-item-container archive-no-box"></li>
</ul>
<ul class="archive-labels">
<li class="archive-item-container"><a class="archive-month-label" href="...">Nov</a></li>
...
<li class="archive-item-container archive-year-label">2012</li>
</ul>
</aside>

Each month/year is represented by a list item, which contains a link to show the box (archive-box) or the label (archive-month-label).

2. Style the Widget

Now that we have our layout, let’s style the widget. It’ll be helpful to define some variables to use in our styles:

$increment: 0.2;
$box-height: 30px;
$base-color: #d5d0cc;
$archive-separator-color: darken($base-color, 40%);
$pad-unit: 1em;

There are also a couple of generic styles to create, one for the archive container and one for individual li elements:

.archives {
float: right;
max-width: 100px;
margin-top: $pad-unit * -0.5;
}

.archive-item-container {
margin-bottom: 0;
height: $box-height + 2;
}

A couple of things to note:

  • Restricting the width of the archives container helps get everything to line up correctly.
  • Don’t forget to add enough pixels to the height of the li to accommodate any borders you may have. In my case, I’ll have 1px borders both for top and bottom, so I added 2px to the height.

Styling the Boxes

To make this widget look like a bar chart, we need to vary the width of each box depending on how many posts there are. We can define the width using the following formula where x is the number of posts:

width(x) = base-value * (1 + increment * x)

Using this formula, I created 12 classes: 11 to display months with 0 to 10 posts (width-*) and 1 to display months which exceed 10 posts (width--). I chose to cap it at 10 because I rarely exceed 10 posts in a given month. If you need more than that, feel free to define more classes.

.archive-boxes {
border-right: 2px solid $archive-separator-color;
padding-right: $pad-unit * 0.3;
float: left;

.width-0 { width: 0; }
.width-1 { width: $pad-unit; }
.width-2 { width: $pad-unit * (1 + $increment * 1); }
.width-3 { width: $pad-unit * (1 + $increment * 2); }
.width-4 { width: $pad-unit * (1 + $increment * 3); }
.width-5 { width: $pad-unit * (1 + $increment * 4); }
.width-6 { width: $pad-unit * (1 + $increment * 5); }
.width-7 { width: $pad-unit * (1 + $increment * 6); }
.width-8 { width: $pad-unit * (1 + $increment * 7); }
.width-9 { width: $pad-unit * (1 + $increment * 8); }
.width-10 { width: $pad-unit * (1 + $increment * 9); }
.width--- {
width: $pad-unit * (1 + $increment * 10);
border-left: 1px dashed darken($base-color, 10%) !important;
}
}

With widths taken care of, let’s focus on creating the key class of this widget, archive-box (as well as its boring sibling archive-no-box):

.archive-box {
background-color: darken($base-color, 5%);
border: 1px solid darken($base-color, 10%);
height: $box-height;
margin-right: $pad-unit/2;
float: right;
clear: both;
text-align: left;
font-size: $base-font-size * 0.7;
padding-left: $pad-unit/2;
color: darken($base-color, 40%);

&amp;:hover {
text-decoration: none;
background-color: darken($base-color, 15%);
border: 1px solid darken($base-color, 20%);
}
}

.archive-no-box {
float: right;
clear: both;
}

To make it look like a box, we need to set the height, border and background-color, as well as add some padding / margin. To ensure that the boxes sit flush against the right border, we need to float them right.

Styling the Labels

Styling the list of labels is significantly more straight forward:

.archive-labels {
float: right;
a {
font-size: $base-font-size * 0.8;
color: darken($primary-color, 20%);
}
}

.archive-month-label {
margin-bottom: 0;
}

.archive-year-label {
font-weight: 600;

&amp;:before {
content: "___";
margin-left: $pad-unit * -1.6;
margin-top: $pad-unit * -0.5;
float: left;
color: $archive-separator-color;
}
}

Probably the only notable thing here is that I’m using a :before selector to draw a tick mark in front of the year label.

3. Get The Archives

The last piece of the puzzle is a bit of php code to make the widget work. I should point out that WordPress already has a built-in way to get the archives via [wp_get_archives()](http://codex.wordpress.org/Function_Reference/wp_get_archives). Unfortunately, this function isn’t flexible enough for our purposes, so we’ll need to get the data directly from the database:

<php
get_header();

global $wpdb;

$query = "SELECT YEAR(post_date) AS 'year', MONTH(post_date) AS 'month', COUNT(ID) as post_count"
."FROM $wpdb->posts :
."WHERE post_type = 'post' AND post_status = 'publish' "
."GROUP BY month, year "
."ORDER BY post_date DESC"

$months = $wpdb->get_results($query);

This query returns a $months collection which contains a count of posts by month/year. Using this collection, we can build up the HTML we need:

$link = "";
$month_abbr = "";
$archive_boxes = "";
$archive_labels = "";

foreach($months as $month) {
$link = get_month_link( $month->year, $month->month );
$month_abbr = date('M', mktime(0,0,0,$month->month, 1, $month->year));
$count_class_suffix = ($month->post_count > 10 ? '--' : $month->post_count);

$archive_boxes .= "<li class=\"archive-item-container\"><a class=\"archive-box width-$count_class_suffix\" href=\"$link\">$month->post_count</a></li>";
$archive_labels .= "<li class=\"archive-item-container\"><a class=\"archive-month-label\" href=\"$link\">$month_abbr</a></li>";

// add row for year
if ($month->month == 1) {
$archive_boxes .='<li class="archive-item-container archive-no-box"></li>';
$archive_labels .= "<li class=\"archive-item-container archive-year-label\">$month->year</li>";
}
}

Finally, we can finally insert the archive widget in our page:

<aside class="archives">
<h3>archives</h3>
<ul class="archive-boxes no-bullets">
<?php print $archive_boxes ?>
</ul>
<ul class="archive-labels no-bullets">
<?php print $archive_labels ?>
</ul>
</aside>