DEV Community

Cover image for Let's make and wear those CSS3 progress rings. πŸ’
Vaibhav Khulbe
Vaibhav Khulbe

Posted on • Updated on

Let's make and wear those CSS3 progress rings. πŸ’

I hope you gulped down those two striped candy bars? If not, then just head over to my previous post:

This time around, we're about to become a jeweller to make some progress rings! πŸ’ Thanks to @yuvaraj's comment embedded below, I started coding this little project:

Nice article .. can you write about circular progress bars. ?

So what are we waiting for? Well...a GIF I suppose?

Ring GIF

Your loved ones will have the same reaction when you make this a reality!😏

Finish off the HTML first πŸ€ͺ

Because...well, you need something to style on? What we want to achieve is the following:

Progress ring

For now, let's first focus on making this. The progress animation will come later.

Let's wear those frontend glasses and inspect what we need to make this happen.

Circle in SVG

At its atomic level, we have a circle. But this is not your regular <div> having a border-radius of 50%! We need to go through some cool SVG based tags for a better understanding:

  • <div>: Well, just know it's short for Content Div ision element. οΌˆβŠ™ο½βŠ™οΌ‰
  • <svg>: You use this when you need to have a set of a new coordinate system in your HTML body. Like if you want to define your own width and height and even some inner child elements to an object. Of course, it also acts as an embed to SVG shapes.
  • <circle>: As you might have guessed, you make...well...circles using this tag. They need to have a certain radius value.

So, we'll need:

  • A container to hold the two rings.
  • The two actual rings.
  • For each ring, we need the <svg> element to draw the circular shape with an equally defined value of x and y coordinates along with its radius.
  • For the percentage value, a div with a heading and a <span> having that small "%" sign.

Here we go:

<div class="rings">
    <div class="percent1">
        <svg>
        <circle cx="70" cy="70" r="70"></circle>
        <circle cx="70" cy="70" r="70"></circle>
    </svg>
    <div class="number">
        <h2>75<span>%</span></h2>
    </div>
    </div>
    <div class="percent2">
        <svg>
        <circle cx="70" cy="70" r="70"></circle>
        <circle cx="70" cy="70" r="70"></circle>
    </svg>
    <div class="number">
        <h2>33<span>%</span></h2>
    </div>
</div>          
Enter fullscreen mode Exit fullscreen mode

Let's make it look like a progress ring πŸ’

First, we need to give each of our elements a fixed width and height values so that they fit into the rings container properly. Hence, we select the two rings, the svg and the circle elements as shown:

.percent1, .percent2, svg, circle {
    width: 200px;
    height: 200px;
}
Enter fullscreen mode Exit fullscreen mode

Now let's make that base circle. We position this absolute to the svg element, set its fill or color to none because technically we don't need to have a color on the circle. For the width of the circle, we give it a length of 10 units or 10px. The greater its value, the thicker our circle outline will be. This is controlled by the stroke-width attribute for the svg.

To add that gap you see on the top of each ring, we use the stroke-dasharray and set it to an approximate value. I found 440 to be good. Then for the type of stroke, we need a circular one because it has a rounded corner perfectly suitable to show progress. This is controlled by the stroke-linecap attribute. Here's what we've been up to:

circle {
    position: absolute;
    fill: none;
    stroke-width: 10;
    transform: translate(10px, 10px); /* This is to position it in the center */
    stroke-dasharray: 440;
    stroke-linecap: round;
}
Enter fullscreen mode Exit fullscreen mode

We now use the nth-child() pseudo-class to select the first element i.e. the ring behind the actual progress bar. We don't need any type of gap between the entire ring behind so we put the value of stroke-dashoffset to 0 and give it a suitable stroke color.

Next, we want to make that progress bar over the top of the grey circle. This is the 2nd element in the HTML (remember we had 2 elements for each ring?) so with the same pseudo-class, we give it the gold stroke, an animation which runs for 1.5 seconds linearly and with a 1-second delay. If you're new to animations in CSS, read this documentation from MDN. In the actual animation definition, just reset the stroke-width and the offset to 0 initially.

.percent1 circle:nth-child(2) {
    stroke: gold;
    animation: percent 1.5s linear;
    animation-delay: 1s;
}

/* Animation definition */
@keyframes percent {
    0% {
        stroke-dashoffset: 0;
        stroke-width: 0;
       }
}
Enter fullscreen mode Exit fullscreen mode

Time for some math...πŸ₯΄ To set our progress bar to the exact percentage value defined inside the rings, we set the value of the stroke-dashoffset as: calc(440 - (440 * 75) / 100)

Now what does that freaky formula means is:

  • The calc() function in CSS allows us to perform such type of complex calculations.
  • First, we take the stroke-dasharray value of our circle (440).
  • Next, we multiply it by the percentage value we need to display or render to the actual progress (75).
  • Finally, we divide it with 100 to calculate the actual percentage value i.e. 75%.

So, our first progress ring in CSS looks like this:

.percent1 circle:nth-child(2) {
    stroke-dashoffset: calc(440 - (440 * 75) / 100); /* Here's what's changed */
    stroke: gold;
    animation: percent 1.5s linear;
    animation-delay: 1s;
}
Enter fullscreen mode Exit fullscreen mode

We do a similar thing for the second ring. The only difference is that it's a rose-gold coloured and has a percentage value of 33. It runs for a little longer with .2 seconds greater delay.

.percent2 circle:nth-child(2) {
    stroke-dashoffset: calc(440 - (440 * 33) / 100);
    stroke: salmon;
    animation: percent 1.8s linear;
    animation-delay: 1.2s;
}
Enter fullscreen mode Exit fullscreen mode

There's nothing special about the CSS of actual percentage values. It's just centred in between, have the same color etc.

.number {
    position: relative;
    bottom: 190px;
    right: -50px;
    color: #fff;
}

h2 {
    font-size: 48px;
}

span {
    font-size: 24px;
    opacity: .7;
}

.percent1 span {
    color: gold;
}

.percent2 span {
    color: salmon;
}
Enter fullscreen mode Exit fullscreen mode

Tada! Those CSS rings are here. You made it. πŸ₯³

If, for any reason you are stuck, then here's the CodePen demo for the same:

So, who's ready to wear these rings? πŸ‘€

Tom ring GIF

Looks like Tom already made it!

Where to next? πŸ€”

This was entirely made with CSS, why not use some JavaScript or jQuery (if you don't hate it), to make the numbers go from 0 to 75%? It would look so cool! Get to know more about SVG and its CSS properties:

Thanks for reading, I appreciate it! Have a good day. (βœΏβ—•β€Ώβ—•βœΏ)


May we suggest cleaning out your fans? Or earplugs. 😭

Source: https://t.co/rcuLDnEWdx#DevHumour #Developer #Programming #ICYMI pic.twitter.com/7kZ2UjSxeZ

β€” Microsoft Developer UK (@msdevUK) June 21, 2020

πŸ“« Subscribe to my weekly developer newsletter πŸ“«

PS: From this year, I've decided to write here on DEV Community. Previously, I wrote on Medium. If anyone wants to take a look at my articles, here's my Medium profile.

Latest comments (4)

Collapse
 
emma profile image
Emma Goto πŸ™

Wow that looks great!

Collapse
 
vaibhavkhulbe profile image
Vaibhav Khulbe

Thank you Emma! 😁

Collapse
 
swastika0015 profile image
Swastika Yadav

This is so cool. Why haven't I thought of it.
Definitely gonna try with JS.

Collapse
 
vaibhavkhulbe profile image
Vaibhav Khulbe

Awesome pawsome! 🐾

Paws GIF