Circular progress stepper/bar

I set out to create a circular progress bar using CSS for a site I am working on. This is what I learned in the process.

My requirements:

  1. I wanted to play with CSS gradients so.. it had to be colorful!
  2. Colors should not be repeated in the bar.
  3. The site I am working on requires 4 or so different states, so it's more like a "stepper" than a progress bar.
  4. The progress bar should be animatable.

Initial research

  • React circular progress bar NPM package Functionality-wise pretty much what I wanted (except for the gradients) to do plus more but built with SVG and React. Maybe I could customize it but I felt more like doing something new.
  • Pure CSS radial progress bar Codepen: this one uses the same trick with rotating rectangulars mentioned in Lea Verou's article but for the border. Unfortunately it also uses the clip property which is deprecated.. Also, no gradient.

Possible solutions

This is what "I" came up with with quite some help from the internets.

See the Pen Circular progress stepper by Spyros Gi (@spygi) on CodePen.

  1. Using 2 background linear gradients.
    • I started by using the border-image: linear-gradient with border-radius for rounded corners. However as these [don't work together] (https://stackoverflow.com/questions/5706963/possible-to-use-border-radius-together-with-a-border-image-which-has-a-gradient) I used the workaround in the answers below of 2 circles with different radius, one covering the other. This technique is used in all 3 solutions either as an pseudoelement or a regular one.
    • Because I needed different colors on the bar, I used the method of 2 half circles as shown here from Steve Marx.

Limitations: the calculations get complicated for intermediate steps. Notice that the colors don't always stay in the same position for different percentages because of how gradient works. For example 75% vs 87.5%. Also because it's done with linear gradient the bar is cut of in a weird angle.. you can set another angle in the gradient but for some values it will always look weird. You can stack linear gradients too.

  1. Using stacked radial gradients (it's weird to get the colors you need)

  2. After implementing my solution I came across Lea’s conic gradient polyfill (again mentioned by Sarah Soueidan on her excellent CSS vs SVG roundup). Here you need a normal element because we are using a polyfill.

// written in a super-verbose way
@if $progress == nth($progress-steps, 9) {
  background: linear-gradient($background-color 0%, rgba(nth($progress-colors, 5), 1) 0, rgba(nth($progress-colors, 4), 1) 50%, rgba(nth($progress-colors, 3), 1) 100%);
} @else if $progress == nth($progress-steps, 8) {
  background: linear-gradient($background-color 25%, rgba(nth($progress-colors, 5), 0.5) 25%, rgba(nth($progress-colors, 4), 1) 50%, rgba(nth($progress-colors, 3), 1) 100%);
} @else if $progress == nth($progress-steps, 7) {
  background: linear-gradient($background-color 50%, rgba(nth($progress-colors, 5), 0) 50%, rgba(nth($progress-colors, 4), 1) 50%, rgba(nth($progress-colors, 3), 1) 100%);
} @else if $progress == nth($progress-steps, 6) {
  background: linear-gradient($background-color 75%, rgba(nth($progress-colors, 5), 0) 75%, rgba(nth($progress-colors, 4), 0.5) 75%, rgba(nth($progress-colors, 3), 1) 100%);
}

What I learned

Moar Resources

  • https://css-tricks.com/examples/GradientBorder/