Building any kind of progress bar in pure css is kind of a misnomer because after all it is a representation of some external event, and as such needs to be told what stage to represent. No matter, this is how I went about creating a radial progress bar (or circular progress bar if you prefer) in pure css3 - in a follow-up post I’ll show you how to use javascript to influence the value.

Click here to view an example of the pure css radial progress bar.

This will only work on modern browsers which support css3 animations and transforms. If you would like to support older browsers there are plenty of javascript heavy options available. I’m also only including the -webkit prefixed rules for brevity but, using the appropriate prefixes (or lack thereof), this will also work in Internet Explorer 10+, FireFox 5+, Opera 12+.

Beyond the typical css properties that you should be familiar with I’ll also be using:

The markup is very light with only a wrapper and two divs which represent the progress. You’ll notice that I’m using data-attributes instead of using classes for the animation selectors. I do this because I feel that animations are more representative of a process than styling.

<div class="wrapper" data-anim="base wrapper">
  <div class="circle" data-anim="base left"></div>
  <div class="circle" data-anim="base right"></div>

First our basic styles.

.wrapper {
  width: 100px; /* Set the size of the progress bar */
  height: 100px;
  position: absolute; /* Enable clipping */
  clip: rect(0px, 100px, 100px, 50px); /* Hide half of the progress bar */
/* Set the sizes of the elements that make up the progress bar */
.circle {
  width: 80px;
  height: 80px;
  border: 10px solid green;
  border-radius: 50px;
  position: absolute;
  clip: rect(0px, 50px, 100px, 0px);

Then onto the animation rules.

/* Using the data attributes for the animation selectors. */
/* Base settings for all animated elements */
div[data-anim~=base] {
  -webkit-animation-iteration-count: 1;  /* Only run once */
  -webkit-animation-fill-mode: forwards; /* Hold the last keyframe */
  -webkit-animation-timing-function:linear; /* Linear animation */

.wrapper[data-anim~=wrapper] {
  -webkit-animation-duration: 0.01s; /* Complete keyframes asap */
  -webkit-animation-delay: 3s; /* Wait half of the animation */
  -webkit-animation-name: close-wrapper; /* Keyframes name */

.circle[data-anim~=left] {
  -webkit-animation-duration: 6s; /* Full animation time */
  -webkit-animation-name: left-spin;

.circle[data-anim~=right] {
  -webkit-animation-duration: 3s; /* Half animation time */
  -webkit-animation-name: right-spin;

And finally, the keyframes.

/* Rotate the right side of the progress bar from 0 to 180 degrees */
@-webkit-keyframes right-spin {
  from {
    -webkit-transform: rotate(0deg);
  to {
    -webkit-transform: rotate(180deg);
/* Rotate the left side of the progress bar from 0 to 360 degrees */
@-webkit-keyframes left-spin {
  from {
    -webkit-transform: rotate(0deg);
  to {
    -webkit-transform: rotate(360deg);
/* Set the wrapper clip to auto, effectively removing the clip */
@-webkit-keyframes close-wrapper {
  to {
    clip: rect(auto, auto, auto, auto);

This works by creating two elements with circular borders and then hiding half of each element and storing those underneath the clip of the wrapper div. I animate a css transform to rotate both elements at the same rate until the 50% mark, at which point the first element stops, the wrapper clip is removed, and the second element keeps going to display the remaining 50%. Without javascript and some outside event influencing the display values this is probably more of a pure css loading indicator so in a follow-up post I’ll show you how to use javascript to influence the value of progress bar.

Click here to view an example of the pure css circular progress bar

I haven’t seen this approach used elsewhere yet so if you come across any other pure css radial progress bars or if you end up using this technique I’d very much appreciate if you would let me know so I can check them out. Any questions please comment below or mention me @fromanegg. Thanks for reading!