Star Ratings With Very Little CSS

Chris Coyier //

Star ratings are one of those classic UX patterns that everyone has tinkered with at one time or another. I had an idea get the UX part of it done with very little code and no JavaScript.

The markup uses the unicode entity for a star (☆) right in it. If you have a UTF-8 charset that should be no big deal. Alternatively you could use ☆ (Calculator for that kind of thing). You could use as many stars as you like:

<div class="rating">
<span>☆</span><span>☆</span><span>☆</span><span>☆</span><span>☆</span>
</div>

Now we need to flop out that "hollow" star with a "solid" star on hover (Gallery for finding those sorts of characters). Easy, just drop a pseudo element of a solid star (★) over it on :hover

.rating > span:hover:before {
   content: "\2605";
   position: absolute;
}

Just by virtue of being it being absolutely positioned, the top: 0; left: 0; are implied (in modern browsers, anyway). So the solid star just sits directly on top of the hollow star. You could even change the color or size if you wished.

But what we have so far only works on individual stars. The UX pattern demands that all the stars be filled in. Fo instance, if we hover over the 4th star, the 4th star becomes solid, but also the 1st, 2nd, and 3rd.

Through CSS, there is no way to select a preceding child element. However, there is a way to select succeeding child elements, through the adjacent or general sibling combinators. If we literally reverse the order of the characters, then we can make use of the general sibling combinator to select all the stars that appear before the hovered star visually, but after the hovered star in the HTML.

.rating {
  unicode-bidi: bidi-override;
  direction: rtl;
}
.rating > span:hover:before,
.rating > span:hover ~ span:before {
   content: "\2605";
   position: absolute;
}

That's it! The whole star ratings UX pattern with very little code. Here's the entire bit of CSS to make it work:

.rating {
  unicode-bidi: bidi-override;
  direction: rtl;
}
.rating > span {
  display: inline-block;
  position: relative;
  width: 1.1em;
}
.rating > span:hover:before,
.rating > span:hover ~ span:before {
   content: "\2605";
   position: absolute;
}

View Demo

And here's a Dabblet if you wanna mess with it.

Actual Usage

Chances are, JavaScript is going to be involved with rating stars anyway. When a user clicks a star, the rating is reported back via Ajax, and the widget itself gains a class to permanently display their selected number of stars. With JavaScript already involved, wouldn't it be OK to lean on it for flip-flopping classes around on the stars to make them work? If your app is absolutely dependent on JavaScript to work, then sure, that's fine. If you are interested in building a website that still works without JavaScript, then these Star Ratings are going to need more work. You should look into Lea Verou's example which uses radio buttons, which could be a part of a form that can be submitted to "rate" whatever it is without JavaScript.

Others

After first sharing this on Twitter, a couple of other folks took a crack at it in slightly different ways. @dmfilipenko's Dabblet. @mprogano's Dabblet