In the mad shuffle to push front-end browser technology beyond the limits of sanity, we forgot about calendars. The calendar grid is a common design element, sometimes relegated to a "widget" in a sidebar and other times filling up the screen with excruciating detail about the current month. It's useful: the grid is a design pattern we've translated to the web from the old days of printed calendars (remember those?) and it therefore appeals to the intuition of a wide range of users.
So why are they difficult to create?
I'm not here to advocate putting more calendar grids out there. Use them when it feels good. I am, however, here to propose a solution to the mess of creating these calendars over and over again. The landscape is not looking good; there are viable plug-and-play solutions out there, sure, but they come with a lot of opinions. Opinions about style, markup, API, and data structures can be good or even great, but in this case a heap of concrete decisions detracts from our ability to control this very unique circumstance.
What's the deal?
My first project at P'unk Ave involved developing an event system for a client. I had a blast crafting the back-end code and the editing experience, but when it came time to create the calendar widget in the sidebar I got stuck. A very experienced coworker admitted that this happens: it's best to create it from scratch. No way, I thought. There has to be something out there that I can plug in. So I surfed Github, found a few jQuery plugins, tried them out. Eventually I landed on one that looked kind of like the design… Close enough that I could hack it apart.
So I hacked and I hacked. One problem led to another and I realized– far beyond the point of no return– that I should have taken the advice and built it from scratch.
The markup in the plugin I chose was written with javascript strings (clearly it was not meant to be edited). The classnames weren't terribly semantic. It made assumptions about the structure of my data, which led me to rewrite the server API for sending the event information. It functioned right out of the proverbial box, but that didn't diminish the headache of implementing the design.
I realized two things:
- Implementation is tied heavily to design.
- Calendars are not easy to build.
Yikes.
The implementation of the calendar is tied to the design because when you need element B to light up for an event instead of element A, your javascript (or whatever you're using!) is going to have to know about it. In my research I didn't come across any plugins that let you say "here's the div I want you to attach these event classes to". Along the same lines, you have to generate those date numbers in the boxes that indicate which day the 1st falls on. What element do we put those numbers in? Whichever one the plugin decided they should go in.
Calendars are difficult to build because they involve so much date-crunching. Dates are an unnatural, man-made set of numbers that don't jive well with programmatic math. This leads to a lot of boilerplate, and corners are very tempting to cut when you're working within tight deadlines. Assuming we're creating a month grid that can go forward and backward, it's going to have to be regenerated every time the month is switched. Either we use a templating language or we'll have to do a ton of DOM selecting and replacing.
I have a solution
I've thought hard about this issue. It has kept me up at night. It led me to create another plugin– one that has some loftier concepts than most calendar plugins. It's called CLNDR.js.
CLNDR is a calendar plugin that doesn't have any markup. Instead, the developer supplies an HTML template (using, by default, Underscore.js's awesome templating engine). In return for a template you get a suite of tools for building your calendar.
HTML templates are well-suited to this task because they allow us the flexibility to specify where we want the data to go in our markup. If you're unfamiliar with templates, have a look at this stack overflow discussion.
Take this example, where days is an array of objects our template is given:
days = [ { day: "1" }, { day: "2" }, ... { day: "31" } ]
<% _.each(days, function(day) { %>
<div class="day"><%= day.day %></div>
<% }); %>