Star Ratings With Very Little CSS

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 &#9734; (Calculator for that kind of thing). You could use as many stars as you like:

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.

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.

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.)

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.

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.

Nice solution without rtl!
The only issue is that because .rating is a block element, hovering over a part of the div which is not a star (e.g. Putting your mouse vertically in line with the stars, but on the far left of the page) results in all the stars going gold.

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….

That’s a neat use of :target. One thing to note, that always seems to bite me in the ass when I want to do something cool functionality-wise with :target, is that you can’t prevent the page-jumpdown when the URL hash changes with just CSS (at least as far as I know). So if there is enough page content so that the page scrolls, when clicking the page will jump so that the star is butted up against the top edge.

Ah curses. You’re absolutely right. Thanks for reminding me. I don’t realize it’s jumping to that page anchor when I’m testing it without it being surrounded by content.

Her solution is good moving forward (especially once IE9/10 are forced on people), but the fallback in 8 and below are ugly radio buttons and a text label.

Seems like her solution + using an image in the label + masking the radio button with the label + linking the label to the radio button = solution. I’m 99% sure that’d be the best solution and with some positioning TLC for IE6+ it’d become a standard.

I would have never thought of something like this – awesome solution for it!
Made my own little mods over at http://dabblet.com/gist/1817473 which mainly consists of some design changes (initially dots instead of stars) and also the removal of “unicode-bidi” since I don’t have a clue what it does.

Cool idea, but it’s accessibility is somewhat limited. Screen reader will not say: rating 4 stars, but: star star star star hollow star (if it even knows how to handle the difference between different star characters, which I doubt). Bots neither can make sense out of it. Also for human visitors, can you be sure those stars still make sense, even if their layout gets broken, e.g. if CSS is off or screen very small?

I prefer the old school patter of using numbers for the rating, and then substituting them on the fly with a proper number of stars, and that’s easier to do with background images.

Another alternative to radio buttons is just an anchor on each star which includes a query string for php to save the rating to the database. In fact I think anchors should be used instead of spans and Javascript can prevent default click event.