DEV Community

Cover image for CSS SVG star rating ⭐️
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

CSS SVG star rating ⭐️

Today we will be looking at making an SVG based star rating.
In our example, we will be using three types of stars. (Empty. Half, and full).

Then we will be showcasing some examples of how to use them to show a specific rating.

The end result, as you can see on this Codepen.

Creating the SVG set

As mentioned we will be using three versions, and we will be using SVG Sprites to accomplish this.

<svg id="stars" style="display: none;" version="1.1">
  <symbol id="stars-empty-star" viewBox="0 0 102 18" fill="#F1E8CA">
    <path d="M9.5 14.25l-5.584 2.936 1.066-6.218L.465 6.564l6.243-.907L9.5 0l2.792 5.657 6.243.907-4.517 4.404 1.066 6.218" />
  </symbol>
  <symbol id="stars-full-star" viewBox="0 0 102 18" fill="#D3A81E">
    <path d="M9.5 14.25l-5.584 2.936 1.066-6.218L.465 6.564l6.243-.907L9.5 0l2.792 5.657 6.243.907-4.517 4.404 1.066 6.218" />
  </symbol>
  <symbol id="stars-half-star" viewBox="0 0 102 18" fill="#D3A81E">
    <use xlink:href="#stars-empty-star" />
    <path d="M9.5 14.25l-5.584 2.936 1.066-6.218L.465 6.564l6.243-.907L9.5 0l2.792" />
  </symbol>
</svg>
Enter fullscreen mode Exit fullscreen mode

As you can see we have the following:

  • stars-empty-star: This is our has a very light gold background.
  • stars-full-star: This is actually the same shape, but with a different color.
  • stars-half-star: This is a combination of an empty star at the bottom, and a half star on top of it.

That's going to be our source, and we can use this in the following ways.

Using the SVG stars

The main question is, of course, how can we now showcase our stars?

Lets say you want to show a empty star:

<svg aria-hidden="true" focusable="false" class="rating">
  <use xlink:href="#stars-empty-star" />
</svg>
Enter fullscreen mode Exit fullscreen mode

Or a full star:

<svg aria-hidden="true" focusable="false" class="rating">
  <use xlink:href="#stars-full-star" />
</svg>
Enter fullscreen mode Exit fullscreen mode

Or even the half star:

<svg aria-hidden="true" focusable="false" class="rating">
  <use xlink:href="#stars-half-star" />
</svg>
Enter fullscreen mode Exit fullscreen mode

That works, awesome!

But now we want to make a 5-star rating component, and SVG's tend to crawl on top of each other.

So if we have the following code:

<!-- 2.5 Rating -->
<svg aria-hidden="true" focusable="false" class="rating">
  <use xlink:href="#stars-full-star" />
  <use xlink:href="#stars-full-star" />
  <use xlink:href="#stars-half-star" />
  <use xlink:href="#stars-empty-star" />
  <use xlink:href="#stars-empty-star" />
</svg>
Enter fullscreen mode Exit fullscreen mode

If all sits like this:

SVG Stars

Hmm, weird? It only shows one star?
Correct!

So let's use CSS to fix that.

use {
  &:nth-child(2) {
    transform: translate(20px);
  }
  &:nth-child(3) {
    transform: translate(40px);
  }
  &:nth-child(4) {
    transform: translate(60px);
  }
  &:nth-child(5) {
    transform: translate(80px);
  }
}
Enter fullscreen mode Exit fullscreen mode

Every x child we give 20px offset position.

So now we get this:

SVG Star rating

You can find the rest of the combinations on the Codepen!

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (3)

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

It'd be so nice if we could just do

.star:nth-child(attr(rating)) { ... }
Enter fullscreen mode Exit fullscreen mode

and just use an attribute to do these things.

Setting that aside, here's a few things I don't completely like about this code:

  1. Instead of using nth-child and transform, you could just add x and y attributes to the use element.
  2. Although symbols don't get drawn regardless, it's still a good idea to put them in a <defs> block at the beginning of the document for readability.
  3. You really only need one star; you can just set the fill from CSS and if necessary use a gradient with a hard edge for half stars.

This is what I came up with after some tinkering:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg rating="2" xmlns="http://www.w3.org/2000/svg" version="2" viewBox="0 0 250 50">
    <defs>
        <style>
            .star { fill: gray; }

            [rating="1"] .star:not(:nth-child(n+2)) { fill: gold; }
            [rating="2"] .star:not(:nth-child(n+3)) { fill: gold; }
            [rating="3"] .star:not(:nth-child(n+4)) { fill: gold; }
            [rating="4"] .star:not(:nth-child(n+5)) { fill: gold; }
            [rating="5"] .star:not(:nth-child(n+6)) { fill: gold; }
        </style>
        <path id="star" d="M 25,0 31,18 50,18 34.5,29.3 40.4,47.5 25,36.3 9.5,47.5 15.5,29.4 0,18 19.1,18.2 Z" />
    </defs>
    <g>
        <use class="star" x="0" href="#star" />
        <use class="star" x="50" href="#star" />
        <use class="star" x="100" href="#star" />
        <use class="star" x="150" href="#star" />
        <use class="star" x="200" href="#star" />
    </g>
</svg>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dailydevtips1 profile image
Chris Bongers

Yeah, attribute nth-child would be a nice one, hey.

Your right could actually have swapped for x on the use. You know how you get into this code and debugging, and then it sticks, haha.

As for your one star, that's not completely true. Since you want dynamic colors based on the same, it's almost impossible to overwrite the color then or do the half stars.

I'll put this on my list to refactor to use the x instead of the nth-child.
Also, you could have defs and created fixed defs for each option so you can just use 1 single-use, but that's if you need all of them.

Collapse
 
darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

Half-stars can easily be done using a linear gradient with two stops at 50% (you can even move it around at random). As for the dynamic colours, I don't really get what you mean by that. Unless you want to change the actual shape of the star, almost everything can be done using CSS.