Elastic Calendar Styling with CSS

Guest Author //

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?

View Demo Download Example

 

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 &nbsp;) 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.

Demo & Downloads