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;
}
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
partially broken in Safari 5.1.2: http://cl.ly/DqpH
Hmm…
https://img.skitch.com/20120202-tdahxwtcyf1fdag65uwh2iybmd.jpg
Also, i think, it’s better to use , semantically, no?
<ul>, sorry
For that particular issue, turns out it’s best to at least use
left: 0;for the absolutely positioned filled star.And I’d say no on the unordered list. If anything, an ordered one, but I’d say it’s just some stars you can click, it’s not a “list”.
I love this idea!
If it were me, I’d tack on “cursor:pointer” to the :hover state.
Yes that would be great!
It’s actually off a few pixels in the Chrome (Mac) too.
Nice article. This is very Great to share this useful post. thanks for the share.
I think it won’t work with mobile safari on touch like it does on mine
It’s an awesome technique, but I also get that pixel-offset in Firefox 10 on OS X.
Same here. I’m using Firefox 10 on Ubuntu (Linux)
FYI:
top: 0; left: 0;are never implied. If an element is positioned absolutely, but a horizontal or vertical position is not specified, the element will take the horizontal or vertical position it would have had if the element would have been statically positioned (i.e. in flow).This is the case for all current browsers. (It works for old IE versions as well, but not completely: old IE has issues with absolutely positioned inline elements.)
See also: CSS 2.1: Visual formatting model details – 10.6.4 : Absolutely positioned, non-replaced elements
Extended to use transparent radio buttons to capture and preserve state without needing any JS:
http://dabblet.com/gist/1709019
I’ve only tested this in Chrome, but consider it a proof-of-concept. Will probably require some JS to support old browsers, but it’s a start.
I just tested it in Firefox 9, and it works perfectly there too. Anyone tested it elsewhere?
You gotta save it as a new Dabblet
Ah – I managed to copy the wrong URL. I did save it as a new dabblet:
http://dabblet.com/gist/1722368
Nice Tip ! but do you know why the stars are so ugly in Chrome on Windows
How to make half star active? if i want to give 3.5 star
a gift ;)
Unicode symbol map bookmarklet similar to “copypaste”
http://panmental.de/symbols/info.htm
Someone mentioned semantics earlier, and this only becomes semantic IMO if is involved.
Otherwise, this is awesome if you don’t require a star treatment outside of what an available typeface provides!
ugh.. if <select> is involved
looks like five squares for me. chrome 15
This would be the perfect use case of the not-yet-available
:nth-letter(n)instead of all those spans.If you’re seeing squares it means the system font being rendered in your browser does not support the glyphs at those code points mentioned in the article.
I’m relatively new to the web development scene and I’m having a hard time understanding why avoiding JS with CSS is important. Are users running browsers with JS disabled? That seems very unlikely to me but again, I’m inexperienced in this space.
I will use it on my site. Thanks!
This would be very easy with SVG.
How’s that?
There’s no need to reverse direction to rtl.
See this Dabblet.
Please could you explain how you’ve managed to replicate Chris’ example without reversing the direction to ‘Right-To-Left’? I’ve read the Dabblet but I don’t quite understand what you did there, mate.
Great article. These are good for experiment but I’ll stick with the jQuery ones.
If the goal is merely minimal css, you could get rid of the .rating selector and it’s and add float: right to each span. Another way of flipping around the order. A by product being you would lose the centering….
Neat technique!
Props Chris, you’re so wicked creative with CSS. I would’ve never come up with this solution.
Though, as mentioned in the comments. Aren’t there browser compatibility issues when you’re using the Unicode symbol for a star instead of using an image?
- Rick
It’s a little annoying that the cursor flickers between text and pointer (I’m using chrome) while hovering over the stars. Maybe wrap each one with a div and set the cursor to something consistent?
It does not work in Windows XP. It however works on Windows Vista and Windows 7 because they have bigger / more updated charsets.
Thanks for the fantastic posts very informative and very usefull for us. Thanks for sharing keep posting.
Very good..
Thank you
DigWP
A book and blog co-authored by Jeff Starr and myself about the World's most popular publishing platform.
Quotes on Design
Design, like Art, can be an elusive word to define and an awfully fun thing to have opinions about.
HTML-Ipsum
One-click copy to clipboard access to Lorem Ipsum text that comes wrapped in a variety of HTML.
Bookshelf
Hey Chris, what books do you recommend? These, young fertile mind, these.