This post was co-authored by Chris Coyier and Tim Wright of CSSKarma
A traditional calendar is a grid of numbered boxes on a page. As a web designer, you might go right for a table, and I wouldn’t fault you for that. Tables, though, can sometimes be tough to muscle into shape. The CSS purist in me gets pissed when I set the width of a table (or a cell) and it decides it knows better and grows or shrinks as it sees fit.
You can tackle calendar styling with pure CSS, and I feel it makes just as much sense semantically as a table does. What is a calender, if not an ordered list of days? By using CSS, we can even do some cool things like do all our sizing with ems so our calendar layout will be elastic. That is, grow in both width and height when text is resized in browsers, while greatly increasing accessibility.
Let’s have a look, shall we?
Three Ordered Lists
Thinking semantically a month calendar isn’t a single ordered list but three. Obviously all the days of the month are one ordered list. But months don’t always start on Sundays and end on Saturdays, so there are those “filler” days at the beginning and end of the grid that make more sense as their own lists. Take a look:
<ol class="calendar" start="6">
<li id="lastmonth">
<ol start="29">
<li>29</li>
<li>30</li>
</ol>
</li>
<li id="thismonth">
<ol>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
<li>11</li>
<li>12</li>
<li>13</li>
<li>14</li>
<li>15</li>
<li>16</li>
<li>17</li>
<li>18</li>
<li>19</li>
<li>20</li>
<li>21</li>
<li>22</li>
<li>23</li>
<li>24</li>
<li>25</li>
<li>26</li>
<li>27</li>
<li>28</li>
<li>29</li>
<li>30</li>
<li>31</li>
</ol>
</li>
<li id="nextmonth">
<ol>
<li>1</li>
<li>2</li>
</ol>
</li>
</ol>
And the fully styled CSS:
/*
* CSS Calendar
* Tim Wright
* Chris Coyier
-----------------------------*/
* {margin:0;padding:0;}
body {font:1em/1.4 Verdana, Arial, Helvetica, sans-serif;
background: url(images/bg.jpg) top center no-repeat #545454;}
body * {display:inline;}
ol.calendar {width:52em;margin:0 auto;display:block; min-height: 200px;
background: url(images/tl.png) top left no-repeat; padding: 12px 0 0 20px;}
li {list-style:none;}
p.link {text-align:center;display: block;}
h1 {display: block; width: 200px;height:76px;
background:url(images/july.png);text-indent:-9999px; margin: 15px auto; }
/*
* Day styles
-------------------------*/
li li {width:6em;height:6em;float:left;margin:.2em; padding:.2em;overflow:auto;
background: url(images/day-bg.png) bottom right no-repeat; }
/*
* Day content (UL/OL & P)
-------------------------*/
li li p {font-size:.7em;display:block;}
li li ol {width:auto;}
li li ul li,
li li ol li {font-size:.7em;display:block;height:auto;width:auto; background: none;
margin:0;padding:.2em 0;float:none;}
/*
* Holiday class
-------------------------*/
li li.holiday { }
/*
* Inactive months
-------------------------*/
li#lastmonth li,
li#nextmonth li { background: url(images/day-bg-inactive.png);}
Couple of important things to note here.
Ordered lists generate their own numbering, so technically we could exploit that and use it to automatically number the calendar. Positioning gets a little tricky doing it that way though and you’ll need to have some content inside each list item anyway (at least a ) to get it to render correctly in all browsers. So, it’s best to set the list style to none and just put the day numbers right in the list items. Because of this, with CSS turned off we get this:
1. 1
2. 2
3. 3
…
etc.
Not the end of the world, but because we want our calendar to be as good as it possibly can be even without CSS, you may want to consider just using unordered lists instead so you get bullets.
Also notice the “start” attribute in the <ol> element. This allows your list to literally start at a number other than “1”. This element has recently been deprecated. The reason for this is that lists aren’t always decimal-based, they could be alphabetical or roman numbers or whatever else ordered lists can be. However, there is no CSS alternative for this, so deprecated element it is. (Note: if you wish to keep this start attribute out of your code for validation reasons, Piotr Petrus has a way of applying it via javascript.)
Cross Browser Quirks
Perhaps even more interesting is the cross browser quirks we ran into with this markup. We are aiming for a straight up grid here, so floated blocks are what we are using. But lists are by default block level elements, so in order to get all the blocks in the same grid technically those blocks would need to be sitting right on top of each other to look right. As we were creating this, every browser was getting it right except (interestingly enough) Firefox 2. Firefox 3 was getting it right though.
Tim tricked Firefox 2 into submission by setting all elements on the page to inline:
body * {display:inline;}
Then setting elements back to block as necessary.
An interesting side note about displaying inline on all elements inside the body… Google Analytics code lives inside the body in a <script> tag (which, by default is set to display:none) and displaying inline on <script> will display your embedded JavaScript. This is actually a nice addition into Firefox, especially when writing a tutorial and wanting to show your CSS or JavaScript. You can actually display the embedded CSS/JS code that’s making the page work, very cool.
In a case like that (with JS embedded somewhere in the body) you may want to wrap your calendar in a <div> and use div *{display:inline;}.
Another weird quirk we found was that the combination of display:inline and float:left would reset the counter on each ordered list item to zero (still not sure why – and only testing that in FF3), so you’d end up with a long list of zeros. That was strange but since we were stripping out the list–style, it wasn’t a big issue.
And of course, any web page wouldn’t be complete without an IE quirk. In IE 6&7 the margin for an OL and a UL inside each day squeezes the text slightly, not a big deal, but something to be aware of.
Final Thoughts
We’re not advocating you throw away your table-based calendars in favor of a pure CSS solution. We’re just showing that it can be done and there are even some specific advantages to doing this way if you choose to. For an alternate take, check out Rob’s recent article on CSS Calendar Styling. Overall, this calendar took a little longer then we thought it would, but it turned out pretty well and is a great way to show that you can make anything look however you want with the right CSS and some semantic markup.
Our example uses background images for the days. Because of the elastic nature of the design, those background images need to be significantly larger than the boxes. Many of the newer browsers offer “zooming” which makes this unnecessary, but to accommodate everyone it is the best practice.
Guys, this is really useful. Thank you :)
Nothing says “inspiring” like CSS-Tricks!
I don’t like Calendar in classic webdesign but your tips are very cool, thanks :)
This is neat.. found it on your tweet feeds.. thanks
Nice technique! Would be quite useful if you need to display event data as a list, but also be able to transform the same markup into a grid view. If you’re primarily representing a grid, though, I’ll contend that a table is no less semantically correct (perhaps even more so), since the positions of the days in the grid are bound by the intersections of the weeks (rows) and days (columns) to which they belong.
Sorry guys, great proof of concept and all, but a calendar is a table with two axes, weeks and days. It’s all very well to bastardize the ordered list, but why would you when a table already does what you want with minimal fuss.
Especially the fuss regarding body * {display:inline;} (though why you didn’t choose ol.calendar * {display:inline;} escapes me) and having to include display:none on a script element (which the w3c validator doesn’t like in your example).
I have to say, the elasticity of the calendar intrigues me, and I only wonder whether the same thing is possible with a table (surely you can set a table’s width by ems).
Nice idea, but let’s leave a disclaimer to say that no sane person should implement this.
I recently had to implement some fairly complicated events calendars with different views: monthly, weekly, and daily.
I found that the “rowspan” and “colspan” attributes were very helpful in allowing events to span across multiple time increments (days, hours, etc). I’m fairly convinced that this would have been a nightmare using a list.
I would agree with some of the other commenters that a table is indeed more semantically correct for styling calendars – particularly in a grid format where the two axis have semantic meaning. Using ROWSPAN, COLSPAN, THEAD and TH gives a lot of flexibility as well as semantic meaining.
Wow. I never imagined arguing FOR the use of tables, but I think it provides the most semantically rich relationships.
With all that out of the way, I do think that you have pulled off the list-based calendar quite beautifully and brilliantly! Kudos, and thanks for sharing!
I’m with Phil on this, if css is turned off, nothing really makes sense any more
but this technique could be very useful for tons of other stuff, again great work Chris and Tim.
keep it up.
Let’s not overdo Lists, okay? Tabels do make sense with Calendars…
Oh, this is great dude!
This is great!
I just don’t see why a table should be overused either. I like the concept, and the “proof is in the pudding, but it really has a lot of coding. Not to say it isn’t a good idea. Quite frankly, I just can’t get a table to act like I want it to, it won’t sit or play dead, if you know what I mean. I am actually implementing a CSS based calender into my upcoming Datebook application. This gave me the coding resource I needed to make a better full-month calender.
A calendar is a table. End of story. This hipster lets-reinvent-everything bullsh1t is getting old now.