Let's make and wear those CSS3 progress rings. 💍
Tags: css, html, webdev, tutorial
Published: 2020-06-25 13:27:05

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

{% link https://dev.to/vaibhavkhulbe/let-s-make-and-eat-those-css3-progress-bars-217a %} 

This time around, we're about to become a jeweller to make some progress rings! 💍 Thanks to [@yuvaraj's](https://dev.to/shivarajnaidu) comment embedded below, I started coding this little project:
 
{% devcomment 10k06 %}

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

![Ring GIF](https://media.giphy.com/media/KyCS9bVWntiutYBlh5/giphy.gif)
<figcaption>Your loved ones will have the same reaction when you make this a reality!😏</figcaption>

## Finish off the HTML first 🤪
Because...well, you need something to style on? What we want to achieve is the following:

![Progress ring](https://dev-to-uploads.s3.amazonaws.com/i/hwmfycon5o50z1v0j65x.jpg)
<figcaption>For now, let's first focus on making this. The progress animation will come later.</figcaption>

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

![Circle in SVG](https://dev-to-uploads.s3.amazonaws.com/i/rplr5lbtbw55o8de9x7w.jpg)

At its atomic level, we have a circle. But this is not your regular [`<div>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/div) having a [`border-radius`](https://developer.mozilla.org/en-US/docs/Web/CSS/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>`](https://developer.mozilla.org/en-US/docs/Web/SVG/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>`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/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>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/span)_ having that small "%" sign.

Here we go:

```html
<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>			
```

## 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:

```css
.percent1, .percent2, svg, circle {
    width: 200px;
    height: 200px;
}
```

Now let's make that base circle. We position this [absolute](https://developer.mozilla.org/en-US/docs/Web/CSS/position) to the svg element, set its [`fill`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/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`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-width) attribute for the svg.

To add that gap you see on the top of each ring, we use the [`stroke-dasharray`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/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`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-linecap) attribute. Here's what we've been up to:

```css
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()`](https://developer.mozilla.org/en-US/docs/Web/CSS/: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`](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/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 <circle> 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](https://developer.mozilla.org/en-US/docs/Web/CSS/animation). In the actual animation definition, just reset the `stroke-width` and the offset to `0` initially.

```css
.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()`](https://developer.mozilla.org/en-US/docs/Web/CSS/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:

```css
.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.

```css
.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.

```css
.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:

{% codepen https://codepen.io/kvaibhav01/pen/wvMdrbE default-tab=css %}

So, who's ready to wear these rings? 👀

![Tom ring GIF](https://media.giphy.com/media/1wmOyZYoGzz003R03Y/giphy.gif)
<figcaption>Looks like Tom already made it!</figcaption>

---

## 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:

- [SVG - MDN Docs](https://developer.mozilla.org/en-US/docs/Glossary/SVG)
- [SVG Tutorial - MDN Docs](https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial)
- [SVG Properties and CSS - A guide by CSS Tricks](https://css-tricks.com/svg-properties-and-css/)

_Thanks for reading, I appreciate it! Have a good day. (✿◕‿◕✿)_

---

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="en" dir="ltr">May we suggest cleaning out your fans? Or earplugs. 😭<br><br>Source: <a href="https://t.co/rcuLDnEWdx">https://t.co/rcuLDnEWdx</a><a href="https://twitter.com/hashtag/DevHumour?src=hash&amp;ref_src=twsrc%5Etfw">#DevHumour</a> <a href="https://twitter.com/hashtag/Developer?src=hash&amp;ref_src=twsrc%5Etfw">#Developer</a> <a href="https://twitter.com/hashtag/Programming?src=hash&amp;ref_src=twsrc%5Etfw">#Programming</a> <a href="https://twitter.com/hashtag/ICYMI?src=hash&amp;ref_src=twsrc%5Etfw">#ICYMI</a> <a href="https://t.co/7kZ2UjSxeZ">pic.twitter.com/7kZ2UjSxeZ</a></p>&mdash; Microsoft Developer UK (@msdevUK) <a href="https://twitter.com/msdevUK/status/1274771066871779328?ref_src=twsrc%5Etfw">June 21, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

---

#### [📫 Subscribe to my weekly developer newsletter 📫](https://mailchi.mp/f59beeac6b9b/devupdates)

##### 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](https://medium.com/@vaibhavkhulbe)'s my Medium profile.