CSS: conic-gradient() for a round progress component

CSS: conic-gradient() for a round progress component

Last week I had a really strange project for company building apps using blockchain technology. The task was pretty simple — to write HTML and CSS for the components that were previously designed and... no JavaScript at all. The reason was understandable (and arguable) — security, without client scripts nobody can steal the private keys from the app using any kind of XSS or similar.

But then one of the components surprised me — a round progress bar with more than one color:

conic-gradient.png

... and then I found out that I cannot use inline SVG too ;-))

My first thought was: “Oh, no, I don't want to use rounded borders and make classes for every percentage point possible.” And the second: “hm, maybe I can do that with radial-gradient somehow?” (no, I can't)

But there is another type of gradients, not supported even by Firefox for now: conic-gradient, but Chrome and Safari are doing great, so if you are targeting the latest versions of mobile OSes — you can use them. And if you need it for charts or progress bars like me, you can have a nice fallback to linear-gradient as well. And here's how I did it.

Using CSS Variables

To make it easy to fall back, we'll have to use the same data twice — in the conic-gradient and the linear-gradient. So let's keep it in the CSS variables if you are not targeting IE, of course. The code for my element looks like this:

<div class="chart" style="--approved: 75%; --rejected: 15%;"></div>

Since we have two colors: green (approved) and red (rejected), and gradients are great in handling data in %, then all we have to do is to write some CSS.

Linear-gradient as a graceful degradation

First comes the fallback — a classic linear-gradient with color-stops at the --approved and --approved + --rejected percentages:

.chart { 
  width: 180px;
  height: 50px;
  background: linear-gradient(
    to right,
    green var(--approved),
    red var(--approved),
    red calc(var(--approved) + var(--rejected)),
    #ccc calc(var(--approved) + var(--rejected))
  );
}

Conical gradient for the browsers that supports it

And then the conical variant — checking for the support and then using the same color stops as before:

@supports (background: conic-gradient(#000, #000)) { 
  .chart { 
    width: 144px; 
    height: 144px; 
    padding: 18px; 
    border-radius: 90px; 
    background: conic-gradient( 
      green var(--approved), 
      red var(--approved), 
      red calc(var(--approved) + var(--rejected)), 
      #ccc calc(var(--approved) + var(--rejected)) 
    ); 
  } 
  .chart--data { 
    width: 144px; 
    height: 144px; 
    background: white; 
    border-radius: 90px; 
  } 
}

The result

You can see, that additionally, I've put another element in the middle, to make the “hole” inside the progress, but you can play with the -webkit-mask properties if you need true transparency.

Hope that was helpful for somebody.