CSS: Star Rating


CSS: Star Rating

People like ratings, they like the ability to rate something and provide feedback, and people like to see the feedback to help make an informed decision.

I spent a lot of time digging around on the web trying to find a way to add a rating system to the CMS I was building, and while I personally don't have a need to do a lot with ratings for my own nefarious purposes, for a business one might need such a thing.

A lot of the different systems I could find for handling this were really complicated, in that they relied heavily on Javascript, JQuery, and other software to deal with determining the exact position of the mouse, of the stars on the screen, and how to get the specific number of stars needed, and more.

Then one day I stumbled (almost literally) across a version that relies solely on HTML, styles, and some images. The code I found is by Stephen Morely, and the site I found it on is here: [Star Rating Widget].

Like many folk who provide tools on the web, the developer gave some explanation, but not a lot. I also tried dropping him an email to ask about something at one point and heard nothing back.

The HTML

In order for this to work, there are a couple of different types of tags being used. The first is that you are using are input tags, specifically radiobutton input tags. For the example we'll do five stars. We do need a form, so I am going to try to provide complete code here:

               <form class="form-horizontal" method="post" action="">
                  <p class="content_label" style="margin-bottom: 5px !important;">Rating:</p>
                  <div class="starRating">
                     <input class="star_rate" id="rating5" type="radio" name="rating" value="5">
                     <label for="rating5">5</label>
                     <input class="star_rate" id="rating4" type="radio" name="rating" value="4">
                     <label for="rating4">4</label>
                     <input class="star_rate" id="rating3" type="radio" name="rating" value="3">
                     <label for="rating3">3</label>
                     <input class="star_rate" id="rating2" type="radio" name="rating" value="2">
                     <label for="rating2">2</label>
                     <input class="star_rate" id="rating1" type="radio" name="rating" value="1">
                     <label for="rating1">1</label>
                  </div>
                  <!-- the rest of the form: -->
                  <p class="content_label" style="margin-top: 5px !important; margin-bottom: 5px !important;">Write a review here:</p>
                  <textarea class="form-control" name="comment" rows="5" required></textarea>
                  <p class="content_label" style="margin-top: 5px !important; margin-bottom: 5px !important;">Your Name:</p>
                  <input class="form-control" name="reviewer_name" placeholder="Your Name, ex: John Q. Smith" required />
                  <button name="review_submit" class="btn btn-success btn-sm" style="margin-top: 10px;">
                     <i class="fa fa-check" aria-hidden="true"></i> Post Your Review
                  </button>
               </form>
            

Note that I added a textarea for someone to enter a full review, an input (entryfield) to enter their name, and a button to actually submit the review.

Stephen notes that while the stars are in reverse order in the HTML, the style sheet will actually reverse them so that "1" becomes "5" and so on.

The Styles

If you read Stephen's description on his page (link above), you will see he explains the before and other items to some extent. I have never understood the "not(old)" part of what he's doing here, but it seems to work.

               .star_rate > a
               {
                 color           : #9c3;
                 text-decoration : none;
               }
               
               .starRating:not(old)
               {
                 display        : inline-block;
                 width          : 7.5em;
                 height         : 1.5em;
                 overflow       : hidden;
                 vertical-align : bottom;
                 font-size      : 16px; /* added to enforce appearance at 16px */
               }
               
               .starRating:not(old) > input
               {
                 margin-right : -100%;
                 opacity      : 0;
               }
               
               .starRating:not(old) > label
               {
                 display         : block;
                 float           : right;
                 position        : relative;
                 background      : url('../images/star_white_16.png');
                 background-size : contain;
               }
               
               .starRating:not(old) > label:before
               {
                 content         : '';
                 display         : block;
                 width           : 1.5em;
                 height          : 1.5em;
                 background      : url('../images/star_gold_16.png');
                 background-size : contain;
                 opacity         : 0;
                 transition      : opacity 0.2s linear;
               }
               
               .starRating:not(old) > label:hover:before,
               .starRating:not(old) > label:hover ~ label:before,
               .starRating:not(:hover) > :checked ~ label:before
               {
                 opacity : 1;
               }
            

I added the first style at the top because I discovered that sometimes I would get an underline under one of the stars if I moved the mouse over it. This is not in Stephen's original code. The note about adding the font-size setting to the starRating class is due to the fun of using em for the size of the images -- if the font size for the overall web page is larger (or smaller) the image may be distorted a bit to make it fit. By forcing the font-size to 16px, which is the size of the images, there is little distortion, if any. If you chose to use 24px images, you would want to change the font-size setting to match, and would need to tinker with the height and width settings as well.

The Images

Images for this kind of thing are always fun. I wasn't fond of the star shapes that Stephen chose to use, and so I searched the web and found some other stars. In the images folder for this site I have a set of star images, and I have bundled everything up in a downloadable .zip file if you want to use these. The images are from [Free Stars Icons].

The size of these is 16px by 16px, which is important for the sizing of the whole thing.

See It

The following (placed into a Bootstrap well) is an example of the whole thing:

Notice that as you move the mouse over the stars they change colors from the blank star to the gold/yellow star color. You could change the color by changing the style above to one of the other images.

Another thing to note is that you could also display more stars. The trick to that is the spacing of the star rating part. You can add the extra radio buttons, but if you don't also modify the style, it won't work. I spent quite a bit of time tinkering with it.

Dropping this down to just the star rating part of the form, the example below shows the stars with a sixth star added:

Note that I stated there are six stars there. However, you don't see the sixth one. However if you were to examine the source (Ctrl+U on most Windows-based browsers), you would see the radiobutton is there for the sixth star.

The change would need to be in the style sheet, specifically this code:

               .starRating:not(old)
               {
                 display        : inline-block;
                 width          : 7.5em;
                 height         : 1.5em;
                 overflow       : hidden;
                 vertical-align : bottom;
                 font-size      : 16px; /* added to enforce appearance at 16px */
               }
            

The width property is the one that is getting us. If you change it to 9em; it works. Basically you need 1.5em for each star, so if you wanted 10 stars instead of 5, you would double the width to 15em.

Getting the Rating Value

The nice thing about this, is that underlying it all is a set of radiobuttons, so to get the value and store it in a table you would treat this like any other set of radiobuttons -- the following is PHP code:

               if( !isset( $_POST['rating'] ) )
               {
                  $rating = 0;
               }
               else
               {
                  $rating = $_POST['rating'];
               }
            

If the user did not click on any of the stars, you want to provide a value of zero, which is why the code is written the way it is.

So I Can Receive The Rating, Can I Just Display It?

Why yes, you can. On most (if not all) websites that use ratings it is desirable to display the ratings that have been received from your customers. Otherwise, what's the point?

My example code for this is PHP, if you can translate code to Javascript or some other language, go for it if that is what you need.

               // sort by review_date field in descending order:
               $review_sql = "select * from reviews order by review_date desc";
      
               // run query:
               $review_res = mysqli_query( $connect, $review_sql );
               $num_reviews = mysqli_num_rows( $review_res );
               
               // nothing to display if the value of num_rows is zero ...
               if( $num_reviews > 0)
               {
                  while( $review_row = mysqli_fetch_array( $review_res ) )
                  {
                     // output comment with a 'reply' button
                     echo '<div class="review">';
                     echo '<span class="review_date">' . MySQL2DateTime( $review_row['review_date'] ) . '</span><br />';
                     // ratings needs to loop through the maximum number of stars,
                     // for now that is 5:
                     $max_stars = 5;
                     $empty_star = '<img src="' . $sRootPath . 'images/star_white_16.png" />';
                     $full_star = '<img src="' . $sRootPath . 'images/star_gold_16.png" />';
                     for( $i = 1; $i <= $max_stars; $i++ )
                     {
                        // if we go the other way, zero would give us
                        // a problem
                        if( $i > $review_row['rating'] )
                        {
                           echo $empty_star;
                        }
                        else
                        {
                           echo $full_star;
                        }
                     }
                     echo '<br clear="all" />';
                     
                     echo '<div>'; // for quote and citation (username):
                     echo '<blockquote class="review_quote">';
                     echo htmlspecialchars_decode( $review_row['comment'] );
                     echo '</blockquote>';
                     echo '<p class="review_name">' . $review_row['reviewer_name'] . '</p>';
                     echo '</div>';
                     echo '</div>'; // for the whole review
                  } // outer while loop
                  
               } // endif num_rows2
               else // no records returned:
               {
                  echo '<p>No reviews at this time -- be the first to review us!</p><br />';
               }
            

The code shown would loop through the reviews table that you have, sorted in descending order (by date, which is stored in the table but not shown). The fun part of the code is determining the correct stars to display, which is inside the for loop.

Download The CSS And Images

If you want to just download the whole thing, click the link here and you will have that option. You need to create the HTML yourself, however. [star_rating.zip]

That's About It!

If you wanted to, you could probably tinker with some of this and make it do even more.