Styling ordered list numbers

I’ve always been annoyed by how difficult it is to style the numbers of ordered lists. Quite often a design calls for something other than just a plain figure—a different font, size, colour, background, whatever.

The traditional approach to solving this problem has been to prevent the browser from rendering the numbers of the list items (li elements) and instead hard code the numbers in the text content of the li. That makes it possible to add styling hooks to the number and style away until you’re happy.

Doing it that way works visually, but it isn’t exactly a semantically correct way of using lists. When you view a faked numbered list with CSS disabled you see either a list with the item numbers repeated or a list with bullets and numbers, and that feels backwards to me.

So when I was recently faced with styling list numbers once again I thought I’d have another go at doing it without faking the numbers. Thanks to Internet Explorer finally starting to catch up on CSS and the increasing client acceptance for progressive enhancement, this time I was able to find a workable solution. If you (well, your client) can live with list item numbers not having any special styling in IE 7 and below, this technique should be usable.

The key is using CSS generated content to create and insert the counter numbers after removing the default numbering from the list. I’ve made a Styling ordered list numbers demo page with a simple example to get you started.

The CSS used on the demo page is this:

ol {
	counter-reset:li; /* Initiate a counter */
	margin-left:0; /* Remove the default left margin */
	padding-left:0; /* Remove the default left padding */
}
ol > li {
	position:relative; /* Create a positioning context */
	margin:0 0 6px 2em; /* Give each list item a left margin to make room for the numbers */
	padding:4px 8px; /* Add some spacing around the content */
	list-style:none; /* Disable the normal item numbering */
	border-top:2px solid #666;
	background:#f6f6f6;
}
ol > li:before {
	content:counter(li); /* Use the counter as content */
	counter-increment:li; /* Increment the counter by 1 */
	/* Position and style the number */
	position:absolute;
	top:-2px;
	left:-2em;
	-moz-box-sizing:border-box;
	-webkit-box-sizing:border-box;
	box-sizing:border-box;
	width:2em;
	/* Some space between the number and the content in browsers that support
	   generated content but not positioning it (Camino 2 is one example) */
	margin-right:8px;
	padding:4px;
	border-top:2px solid #666;
	color:#fff;
	background:#666;
	font-weight:bold;
	font-family:"Helvetica Neue", Arial, sans-serif;
	text-align:center;
}
li ol,
li ul {margin-top:6px;}
ol ol li:last-child {margin-bottom:0;}

The key parts are the following:

Follow the links for detailed explanations of the various properties.

You can use pretty much any styling you want on the generated element. One thing to keep in mind is what happens if the list is extremely long—will there be room for three- or four-figure numbers? Not a very common scenario, but keep an eye on the width of the element containing the number.

As I mentioned already, this does not work in IE 7 and older since IE didn’t add support for the content property and the :before and :after pseudo-elements until IE 8. By using conditional comments or CSS hacks to reapply the normal list numbering you can make those older browsers render the list numbers too, just without the pretty styling. Most people should be able to live with that.

Update: The demo page now includes fallback styling for IE7 and older. Thanks to everyone who pointed put that it was missing, and to those of you who alerted me to the nested lists problems which should be fixed now. And thanks to Eric Meyer for reminding me that for a while some browsers didn’t let you position generated content. Camino 2 and Firefox 3.0 are examples of such browsers. I added some padding and a right margin to the generated number to make it look a bit less off in those browsers.

Posted on May 25, 2011 in CSS