Cover image for Let's make and wear those CSS3 progress rings. 💍

Let's make and wear those CSS3 progress rings. 💍

vaibhavkhulbe profile image Vaibhav Khulbe Updated on ãƒŧ6 min read

Make some CSS3 progress bars (3 Part Series)

1) Let's make and eat those CSS3 progress bars. 🍭 2) Let's make and wear those CSS3 progress rings. 💍 3) 4 more random CSS3 progress bars. 😋

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">
        <circle cx="70" cy="70" r="70"></circle>
        <circle cx="70" cy="70" r="70"></circle>
    <div class="number">
    <div class="percent2">
        <circle cx="70" cy="70" r="70"></circle>
        <circle cx="70" cy="70" r="70"></circle>
    <div class="number">

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;

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;

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;

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;

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;

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;

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. (âœŋ◕â€ŋ◕âœŋ)

ðŸ“Ŧ 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.

Make some CSS3 progress bars (3 Part Series)

1) Let's make and eat those CSS3 progress bars. 🍭 2) Let's make and wear those CSS3 progress rings. 💍 3) 4 more random CSS3 progress bars. 😋

Posted on by:

vaibhavkhulbe profile

Vaibhav Khulbe


ðŸ‘Ļ‍ðŸ’ŧ Freelance web developer/designer/writer ✍ | 📧 Subscribe to my weekly dev newsletter with 50+ resources: https://mailchi.mp/f59beeac6b9b/devupdates


markdown guide

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


Awesome pawsome! ðŸū

Paws GIF