How to Make Easy & Flexible Star Ratings

About Harrison Shoff

Here’s a quick tip explaining how to create some nice star ratings.

Stars

Setting the Stage

The goal is to avoid having a sprite that looks like this:

Big

This method works, but has some drawbacks: it uses a lot of pixels, it’s a nightmare to maintain/update, and it forces you to commit to a set rating size.

Let’s do better.

Each star has two states: filled and empty. We’ll start here.

Here’s the sprite we’ll be working with (super sized stars courtesy of Airbnb’s marvelous designer Steph Tekano):

Sprite

Requirements

  • Should support any number of stars. We use five star ratings right now, but another module could potentionally use ten stars or four stars later down the road.
  • Should support fractional stars. We use half stars right now, but again, this could change later on and it should be easy.
  • No JavaScript. These are static ratings. There’s no interaction with them, so they should be rendered on page load.

Think Layers

There are two layers, the empty layer and the filled layer. The filled layer will sit on top of the empty layer.

Layers

We’ll use repeating background images to create the number of stars we want for both the empty and filled layers. The empty layer will be a fixed width based on the number of stars we’re rating. So for five stars, the width of the empty layer will be 5 x (the width of one star).

The filled layer will have a variable width that is dependent on what the rating is.

The magic happens in the styling.

Markup

Let’s start with the markup.

Pretty straightforward.

“filled” and “empty” are pretty common class names. To be extra careful that you don’t override any other selectors of the same name you can give them an additional class called “stars”.

SASS

Now let’s do some styling.

We use Sass beacuse it makes life easier. Some of the big benefits are Nesting, Variables, Functions, Importing, and Mixins. It compiles everything to CSS. I recommend it because you can get things done faster. Less time styling is a good thing.

The first step is to create some variables to specify the width of a star & background offset (from the sprite), the number of stars, and the number of steps (I used 2 because I want half star ratings).

Then I created a mixin called “filled” which figures out the width of the filled div based on the size of the star, the number of steps, and the rating number $n (default is 0). The power of the mixin is that you don’t have to do any math.

Both the filled and empty classes use /images/sprite.png as their background image. The empty class offsets the sprite vertically by the height of a star.

Here’s what the generated CSS looks like:

The Missing Piece

You can see from stars.scss that I created classes specific to the star rating: filled_5, filled_6, filled_7 etc. Those numbers are the star rating stored as attributes on whatever the model is. All we need to do is output that rating into the markup.

Going Further

It would be a simple refactor to make a Rails helper method to output the markup with a call like <%= star\_rating(@obj.rating) %> in a view. For more bonus points you could turn stars.scss into a partial called _stars.scss and then @import it into other scss files when needed in other modules.

 

Lots of folks handle this sort of thing in different ways. This happens to be the way I like to do it because it’s flexible, tries not to repeat itself, and requires one small sprite. Hope it helps. Thanks for reading!

Starss

4 comments

About Harrison Shoff

Speak Your Mind

*

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four 
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Comments

  1. jdavid

    you could also use other css3 tricks, for instance, using::after – pseudo selector:nth-of-type():active:focusi would use the radio button as a base to be syntactically correct.

  2. Eduardo Carvalho
  3. Ricardo

    The CSS is nice, but you should consider adding some actual content for accessibility, either text or a title attribute, e.g. https://gist.github.com/1289062Also, I found the lack of microdata/microformats surprising, if I had to guess I’d said AirBNB used them.

  4. producersteve

    @Ricardo – I agree with both your points; microdata/microformats should be included in this somehow as well as text should be included for accessibility.A friend of mine created what I felt was a pretty good solution that involved using the UTF8 ★ character for filled-in stars in li elements, then added an absurdly large text-indent value and used background images for the li’s in CSS.The other thing I find somewhat surprising is that they were successful with a layering approach. Frankly, I would expect pixel differences in different browsers’ rendering engines to make this approach blow up in your face for every browser except the one you develop in.