An updated and more cross-browser compatible version of the CSS star rater can be found here.
This is my first contribution to the CSS loving community. So, many of you have seen a blog. Many of you have seen comments and reviews on sites such as Netflix and Amazon. Most of these reviews, as in Amazon’s case or Movies, as in Neflix’s case come with a handy dandy rating. I’ve even seen the star rating system used at msn.com. Many of these solutions may use JavaScript or just have an individual hover state for a single star. The question I pose is…. Can you create a star rating using only CSS. I’m talking the kind that when you hover over the 4th star, there are four stars that show up on the hover state. Well, I’m glad you asked because that is just the question I intend to answer.
But! Before we get into the action, I know I like to see a working example first, so here, check this out.
Step 1: XHTML
First, lets get to structure. A star rating simply is a list of links, correct? So, we will create a simple list of links. We will give it a class of “star-rating” and class the links and title them appropriately. Lets do this. Viola:
xhtml:
- <ul class=’star-rating’>
- <li><a href=’#’ title=’Rate this 1 star out of 5′ class=’one-star’>1</a></li>
- <li><a href=’#’ title=’Rate this 2 stars out of 5′ class=’two-stars’>2</a></li>
- <li><a href=’#’ title=’Rate this 3 stars out of 5′ class=’three-stars’>3</a></li>
- <li><a href=’#’ title=’Rate this 4 stars out of 5′ class=’four-stars’>4</a></li>
- <li><a href=’#’ title=’Rate this 5 stars out of 5′ class=’five-stars’>5</a></li>
- </ul>
This list that we create would potentially have href’s pointing to the correct page. Also, you could use Ajax to attach a different action to each link, but we will not cover that here. It suffices to say that we will just point them to the correct page for that rating.
Step 2: Graphics
We don’t event know what this thing is going to look like. I’d love to make this a transparent PNG, but because an evil nerd with lots of money has a proprietary browser with its own set of quirky rules, transparent PNG images don’t behave well when using CSS background manipulations, such as when using background-position. So, we will go with the all so ancient GIF. Bam! Here is the GIF image:

This image has a hover state, which is the bottom star and a normal state, which is the top star.
Step 3: CSS
Lets get into the meat of the CSS. First, I always style the parent-most element, in this case the UL (Unordered List) with a class of ’star-rating’:
- .star-rating{
- list-style: none; – turn off the default list image bullets
- margin: 3px; – I wan’t some space around this thing
- padding: 0px; – I’m anal. I’m pretty sure OL’s have a default padding of 0px, but we’ll set it to 0px just to be safe
- width: 100px; – This list is 5 stars, each star is 20px, therefore it should be 5 x 20px = 100px wide
- height: 20px; – The height of each star is 20px. Since this is a horizontal list, we will set the list height to the height of the star.
- position: relative; – Very important. We will be using absolute positioning later. We want to use relatively-absolute positioning.
- background: url(star_rating.gif) top left repeat-x; – By repeating this image horizontally, the list will appear to have five stars.
- }
So, in review, to any object with a class of “star-rating”, we did the following:
- removed the bullets
- gave it a small margin
- removed the list indent entirely
- made the entire list 100 pixels wide and 20 pixels high
Now for the list items, here is the css:
- .star-rating li{
- padding:0px; – no padding at all
- margin:0px; – no margin at all
- /*\*/ – Backslash hack, this causes IE5 Mac NOT to see this rule
- float: left; – for any other browser, we are going to float left, this makes a horizontal list
- /* */ – end the IE5 Backslash hack
- }
Basically, this css tells all list items within an object with a class of “star-rating” to be horizontal, rather than vertical. There is a CSS hack called the backslash hack to make the float: left; rule invisible to IE5 Mac.
Now for the anchor items within the list items, here is the css:
- .star-rating li a{
- display:block; – we want a block item, so that we can mess with its height and width
- width:20px; – easy stuff, we want the width to be the same as the star width
- height: 20px; – same as the width
- text-decoration: none; – remove the underline from the link
- text-indent: -9000px; – indent the text off the screen using a image-replacement technique, we dont want to see the text anymore.
- z-index: 20; – we’ll come back to this
- position: absolute; – we can now control the exact x and y coordinates of each star, relative to the parent UL
- padding: 0px; – once again, we don’t need any padding
- background-image:none; – we will not show the star
- }
- .star-rating li a:hover{
- background: url(star_rating.gif) left bottom; – this is where the magic is
- z-index: 1; – move this star to the bottom of the z-index stack
- left: 0px; – move this star all the way to the left, aligned with the side of the UL parent item
- }
In review, the CSS for the anchor items is not too complex. Basically we are making the anchor items as big as the star graphic and pushing their inner text off the screen. You will not see the actual anchor items, but rather it is like having a set of invisible hot spots.
As for the :hover state, this is where the CSS magic is. When the users hover, the specific link that they are hovering gets pushed all the way to the bottom of the z-index stack. The link then has a background image load in, but the background is shifted 20 pixels down to the bottom, which now shows us our over state image. The link gets pushed all the way to the left hand side.
Now, I know what you are thinking. You are thinking, wait, if the link shifts all the way to the left, then the :hover state will end and then the star image will dissappear! You are correct, and very smart too. But, wait! We have not finished the CSS yet. Lets see if the next CSS Rules change what is happening at all.
Also, you may think, why repeat the image horizontally if the link is still 20 pixels? Why even put the repeat-x rule there? Another good question. The goal when you hover over the link is to do this:
- push the link all the way to the left – weve done this
- make the over state of the image show – weve done this
- make the link as wide as however many stars it should have – i.e. 3 stars = 3 x 20px per star = 60px – we have NOT done this yet
- repeat the star graphic horizontally so it looks like we have that many stars – we’ve done this
So, on to the remaining CSS. Since each star has a different width based on how many stars it is, we have to have an individual rule for each star link. This is why we put a class on each different link.
- .star-rating a.one-star{
- left: 0px;
- }
- .star-rating a.one-star:hover{
- width:20px;
- }
- .star-rating a.two-stars{
- left:20px;
- }
- .star-rating a.two-stars:hover{
- width: 40px;
- }
- .star-rating a.three-stars{
- left: 40px;
- }
- .star-rating a.three-stars:hover{
- width: 60px;
- }
- .star-rating a.four-stars{
- left: 60px;
- }
- .star-rating a.four-stars:hover{
- width: 80px;
- }
- .star-rating a.five-stars{
- left: 80px;
- }
- .star-rating a.five-stars:hover{
- width: 100px;
- }
This last block of CSS is fairy easy to explain. The goal here is to make each link as wide as how many stars it is x 20px only when the link is hovered. This way a link with four stars will be 0px from the left and 80px wide. This causes the link to still be located under the mouse, so we do not lose our :hover state. So, each link on :hover needs to be as wide as its number of stars x 20px, i.e. for two stars, 2 x 20px = 40px, for three stars 3 x 20px = 60px and so on and so forth.
You’ll notice that there is also a style for each link in its normal, NON-hovered state. This is because each link is absolutely positioned. If we did not put a left coordinate on them, it would be hard to tell where each link would be horizontally positioned in each browser. This way, we can have fine-tuned control of the horizontal positioning of each link.
And thats it! For a live page that contains a working sample, go here. If you have any questions at all, please feel free to comment.








93 Comments