Have one <span>
element nested inside another.
The outer <span>
has a background image of empty stars.
The inner <span>
has a background image of a colored-in star, and a width:
element that is programmatically set when you layout the page. For example, width:60.0%
could be used for a 3/5 star rating.
For a small optimization, you may even use start with an image that is only one star and use repeat-x to get all 5 stars. You could also have an image that includes both colored and filled-in stars vertically, and crop at display time to determine which version of the star you are referencing.
Example 1:
For example, consider the following CSS:
span.rating-frame, span.rating-fill {
background: url(../star.png) 0 -16px repeat-x;
display: block;
width: 80px; /* this is 5 times the width of the star, for 5 stars total */
height: 16px;
}
span.rating-fill {
background-position: 0 0; /* use the (filled-in) version from the top of the image */
}
and the following HTML:
Average Rating: 4/5
<span class="rating-frame">
<span style="width:80.0%;" class="rating-fill"> </span>
</span>
User A: 5/5
<span class="rating-frame">
<span style="width:100.0%;" class="rating-fill"> </span>
</span>
User B: 3/5
<span class="rating-frame">
<span style="width:60.0%;" class="rating-fill"> </span>
</span>
This assumes that star.png is a 16x32 image with the filled-in star on top.
The value for the width attribute is set by userRating/maxPossibleRating
rounded to an appropriate precision. Of course, where you show the overall average rating, it's set by averageUserRating/maxPossibleRating
.
Example 2:
OP reports using <span>☆</span>
instead. The following pseudocode might be helpful in page generation there.
for (i = 1 to rating inclusive) {
print "<span>★</span>"
}
//if including a half-star, replace this comment with the code to do so.
for (i = rating+1 to maxRating inclusive) {
print "<span>☆</span>"
}
but unless you have a half-filled star character, you won't be able to use that in your output. You might try using the ½ character instead.
Example 3:
The eBay example OP links to uses source like this:
<span class="display-text-rating">3.7 stars</span>
<div class="star-rating-section" title="3.7 out of 5 stars">
<span class="star-rating" role="img" aria-label="3.7 stars">
<i class="fullStar"></i>
<i class="fullStar"></i>
<i class="fullStar"></i>
<i class="midStar"></i>
<i class="emptyStar"></i>
</span>
</div>
The first line is the printed text showing the rating in text form.
The div title shows that information on mouse hover.
The elements w/class attributes inside the span could easily be programmatically generated as in example 2. The CSS could be set to use different images for fullStar, midStar, and emptyStar. This generation process could be repeated for each user in the section where the users' individual ratings go.
Example 3 has the disadvantage of visually rounding to the nearest half-star (and example 2, to the full star), while example 1 permits more fine-grained distinction of fractional stars without requiring extra programming or image creation. Deciding on the appropriate level of rounding is a question for UX.SE (and if anyone does care enough to ask it, please edit in a link here).