Search code examples
htmlcsscharts

Creating a static pie chart with CSS


I am attempting to create a pie chart using css and html. I would simply be displaying a few static numbers therefore I am trying to keep it relatively simple and not use any animations.

I'm currently running into a road block on how to create my desired look. The code snippet below works exactly as I would like it to, the issue with this is that conic-gradient is not supported with firefox and internet explorer which will be an issue with this project.

.progress-circle {
  --d: 50px;
  --color: #002F65;
  --progress: 40;
  border-radius: var(--d);
  height: var(--d);
  width: var(--d);
  background: conic-gradient( var(--color) calc(calc(var(--progress) / 100) * 360deg), transparent calc(calc(var(--progress) / 100) * 360deg));
}
<div class="progress-circle"></div>


I have been searching for an alternative that resembles the example above which had lead me to this article: designing simple pie charts with css

My issue with this is that the way to calculate the percenage growth of the pie chart seems to be not compatible with what I am trying to accomplish. as it is determined by transform: rotate(.1turn);

My main question is it possible to make conic-gradient compatible with other browsers? If not, what would be the best way to approach making a pie chart with css to closely resemble the first example?

For context I will be passing data from an array to determine the percentage of the pie chart.

.pie {
  width: 100px; height: 100px;
  border-radius: 50%;
  background: yellowgreen;
  background-image:
  linear-gradient(to right, transparent 50%, #655 0);
}

.pie::before {
  content: "";
  display: block;
  margin-left: 50%;
  height: 100%;
  border-radius: 0 100% 100% 0 / 50%;
  background: #655;
  transform-origin: left;
  transform: rotate(.1turn);
}
<div class="pie"></div>


Solution

  • Here is an idea based on this previous answer

    .box {
      /* percentage to degree
        --s:0 for [0% 50%]
        --s:1 for [50% 100%]
       */
      --v:calc( ((18/5) * var(--p) - 90)*1deg);
    
      width:100px;
      height:100px;
      display:inline-block;
      border-radius:50%;
      background:
        linear-gradient(var(--v), yellowgreen 50%,transparent 0) 0 /calc((1 - var(--s))*100%),
        linear-gradient(var(--v), transparent 50%,#655        0) 0 /calc(var(--s)*100%),
        linear-gradient(to right, yellowgreen 50%,#655 0);
    }
    <div class="box" style="--p:5;--s:0"></div>
    <div class="box" style="--p:20;--s:0"></div>
    <div class="box" style="--p:50;--s:0"></div>
    <div class="box" style="--p:70;--s:1"></div>
    <div class="box" style="--p:95;--s:1"></div>

    We can optimize the code using min() and keep the use of only one variables but you need to pay attention to the support: https://caniuse.com/#feat=css-math-functions

    .box {
      /* percentage to degree  */
      --v:calc( ((18/5) * var(--p) - 90)*1deg);
    
      width:100px;
      height:100px;
      display:inline-block;
      border-radius:50%;
      background:
        linear-gradient(var(--v), yellowgreen 50%,transparent 0) 0 /min(100%,(50 - var(--p))*100%),
        linear-gradient(var(--v), transparent 50%,#655        0) 0 /min(100%,(var(--p) - 50)*100%),
        linear-gradient(to right, yellowgreen 50%,#655 0);
    }
    <div class="box" style="--p:5;"></div>
    <div class="box" style="--p:20;"></div>
    <div class="box" style="--p:50;"></div>
    <div class="box" style="--p:70;"></div>
    <div class="box" style="--p:95;"></div>

    Another idea using pseudo element with more support:

    .box {
      /* percentage to degree  */
      --v: calc( ((18/5) * var(--p) - 180)*1deg);
      
      width: 100px;
      display: inline-flex;
      border-radius: 50%;
      overflow: hidden;
      background: linear-gradient(to right, yellowgreen 50%, #655 0);
    }
    
    .box::before,
    .box::after {
      content: "";
      width: 50%;
      padding-top:100%;
      transform: rotate(var(--v));
    }
    
    .box::before {
      background: 
        linear-gradient(yellowgreen 0 0) 
        0 / calc((50 - var(--p))*1%);
      transform-origin: right;
    }
    
    .box::after {
      background: 
        linear-gradient(#655 0 0)       
        0 / calc((var(--p) - 50)*1%);
      transform-origin: left;
    }
    <div class="box" style="--p:5;"></div>
    <div class="box" style="--p:20;width:150px;"></div>
    <div class="box" style="--p:50;width:120px;"></div>
    <div class="box" style="--p:70;"></div>
    <div class="box" style="--p:95;width:80px;"></div>

    CSS pie chart