Saturday, April 5, 2008

One Tag Solution: Simple CSS Ratings

The 4- or 5-star rating has become the default for conveying popularity or relevance across the web. There are many ways to render a rating using stars in HTML and CSS. However, I have not seen a solution that is simultaneously simple, optimized, semantic, and accessible. Therefore, I set to work to uncover my own. The result looks like this: (The image 'none.gif' is a 1x1 transparent gif file)

<img class="rating" alt="3.5" src="/none.gif" />

The initial goal was to start by rendering a rating using only a single HTML tag that both visually displays the rating and retains its numerical value. The tag to use was very obviously the <img> tag. It is meant to display visual information and the 'alt' attribute is the perfect way to convey the numerical value of the rating. For slow-loading webpages, the numerical value of the rating will be displayed until the images load in.

The next objective was to make use of image spriting to contain the stars so that only one image has to be loaded and cached for all ratings. There are several different approaches to this. The image sprite I chose to make use of looks like this:

The idea is to set the width of the image to be 5-stars wide and then slide this sprite around to get the desired rating.

Building the CSS for this is rather simple. I will demonstrate the intended, but IE6-incompatible approach first. IE6 is the only modern browser that does not support the attribute selector syntax.

img.rating {
background: transparent url(stars.gif) no-repeat scroll;
width: 100px;
height: 20px;
img.rating[alt="0.0"] { background-position: -100px 0; }
img.rating[alt="0.5"] { background-position: -80px -20px; }
img.rating[alt="1.0"] { background-position: -80px 0; }
img.rating[alt="1.5"] { background-position: -60px -20px; }
img.rating[alt="2.0"] { background-position: -60px 0; }
img.rating[alt="2.5"] { background-position: -40px -20px; }
img.rating[alt="3.0"] { background-position: -40px 0; }
img.rating[alt="3.5"] { background-position: -20px -20px; }
img.rating[alt="4.0"] { background-position: -20px 0; }
img.rating[alt="4.5"] { background-position: 0 -20px; }
img.rating[alt="5.0"] { background-position: 0 0; }

The less than desirable CSS solution that works with IE6 is to simply add an additional class to the image indicating which rating is being displayed.

img.rating {
background: transparent url(stars.gif) no-repeat scroll;
width: 100px;
height: 20px;
img.rating-0\.0 { background-position: -100px 0; }
img.rating-0\.5 { background-position: -80px -20px; }
img.rating-1\.0 { background-position: -80px 0; }
img.rating-1\.5 { background-position: -60px -20px; }
img.rating-2\.0 { background-position: -60px 0; }
img.rating-2\.5 { background-position: -40px -20px; }
img.rating-3\.0 { background-position: -40px 0; }
img.rating-3\.5 { background-position: -20px -20px; }
img.rating-4\.0 { background-position: -20px 0; }
img.rating-4\.5 { background-position: 0 -20px; }
img.rating-5\.0 { background-position: 0 0; }

The modified HTML code would look like:

<img class="rating rating-3.5" alt="3.5" src="/none.gif" />

In the end, the result is a simple, semantic rating in one tag and one image. For part 2, I will demonstrate how to turn this into a rating you can pick from.