CSS Only Donut Chart With Custom Slices

Category: Chart & Graph , CSS & CSS3 | May 6, 2020
Author: Jerry Low
Views Total: 6,182 views
Official Page: Go to website
Last Update: May 6, 2020
License: MIT

Preview:

CSS Only Donut Chart With Custom Slices

Description:

A minimal clean donut chart to represent percentage values as slices using pure CSS/SCSS.

How to use it:

1. Create the HTML for the donut chart and define the percentage value for each slice using CSS variables as follows:

<div class="donut" style="--first: .40; --second: .33; --third: .12; --fourth: .08; --fifth: 0.07">
  <div class="donut__slice donut__slice__first"></div>
  <div class="donut__slice donut__slice__second"></div>
  <div class="donut__slice donut__slice__third"></div>
  <div class="donut__slice donut__slice__fourth"></div>
  <div class="donut__slice donut__slice__fifth"></div>
  <div class="donut__label">
    <div class="donut__label__heading">
      CSSScript.Com
    </div>
    <div class="donut__label__sub">
      Donut Chart
    </div>
  </div>
</div>

2. The main CSS styles for the donut chart. Feel free to override the CSS styles as shown below:

.donut {
  --donut-size: 300px;
  --donut-border-width: 20px;
  --donut-spacing: 0;
  --donut-spacing-color: 255, 255, 255;
  --donut-spacing-deg: calc(1deg * var(--donut-spacing));
  border-radius: 50%;
  height: var(--donut-size);
  margin: 40px;
  position: relative;
  width: var(--donut-size);
}

.donut__label {
  left: 50%;
  line-height: 1.5;
  position: absolute;
  text-align: center;
  top: 50%;
  -webkit-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
  width: 80%;
}

.donut__label__heading {
  font-size: 24px;
  font-weight: 600;
}

.donut__label__sub {
  color: #666666;
  font-size: 14px;
  letter-spacing: 0.05em;
}

.donut__slice {
  height: 100%;
  position: absolute;
  width: 100%;
}

.donut__slice::before,
.donut__slice::after {
  border: var(--donut-border-width) solid rgba(0, 0, 0, 0);
  border-radius: 50%;
  content: '';
  height: 100%;
  left: 0;
  position: absolute;
  top: 0;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
  width: 100%;
}

.donut__slice::before {
  border-width: calc(var(--donut-border-width) + 1px);
  box-shadow: 0 0 1px 0 rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
}

.donut__slice__first {
  --first-start: 0;
}

.donut__slice__first::before {
  border-top-color: rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
  -webkit-transform: rotate(calc(360deg * var(--first-start) + 45deg));
          transform: rotate(calc(360deg * var(--first-start) + 45deg));
}

.donut__slice__first::after {
  border-top-color: #ff6838;
  border-right-color: rgba(255, 104, 56, calc(100 * (var(--first) - .25)));
  border-bottom-color: rgba(255, 104, 56, calc(100 * (var(--first) - .5)));
  border-left-color: rgba(255, 104, 56, calc(100 * (var(--first) - .75)));
  -webkit-transform: rotate(calc(360deg * var(--first-start) + 45deg + var(--donut-spacing-deg)));
          transform: rotate(calc(360deg * var(--first-start) + 45deg + var(--donut-spacing-deg)));
}

.donut__slice__second {
  --second-start: calc(var(--first));
  --second-check: max(calc(var(--second-start) - .5), 0);
  -webkit-clip-path: inset(0 calc(50% * (var(--second-check) / var(--second-check))) 0 0);
          clip-path: inset(0 calc(50% * (var(--second-check) / var(--second-check))) 0 0);
}

.donut__slice__second::before {
  border-top-color: rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
  -webkit-transform: rotate(calc(360deg * var(--second-start) + 45deg));
          transform: rotate(calc(360deg * var(--second-start) + 45deg));
}

.donut__slice__second::after {
  border-top-color: #ffc820;
  border-right-color: rgba(255, 200, 32, calc(100 * (var(--second) - .25)));
  border-bottom-color: rgba(255, 200, 32, calc(100 * (var(--second) - .5)));
  border-left-color: rgba(255, 200, 32, calc(100 * (var(--second) - .75)));
  -webkit-transform: rotate(calc(360deg * var(--second-start) + 45deg + var(--donut-spacing-deg)));
          transform: rotate(calc(360deg * var(--second-start) + 45deg + var(--donut-spacing-deg)));
}

.donut__slice__third {
  --third-start: calc(var(--first) + var(--second));
  --third-check: max(calc(var(--third-start) - .5), 0);
  -webkit-clip-path: inset(0 calc(50% * (var(--third-check) / var(--third-check))) 0 0);
          clip-path: inset(0 calc(50% * (var(--third-check) / var(--third-check))) 0 0);
}

.donut__slice__third::before {
  border-top-color: rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
  -webkit-transform: rotate(calc(360deg * var(--third-start) + 45deg));
          transform: rotate(calc(360deg * var(--third-start) + 45deg));
}

.donut__slice__third::after {
  border-top-color: #97c95c;
  border-right-color: rgba(151, 201, 92, calc(100 * (var(--third) - .25)));
  border-bottom-color: rgba(151, 201, 92, calc(100 * (var(--third) - .5)));
  border-left-color: rgba(151, 201, 92, calc(100 * (var(--third) - .75)));
  -webkit-transform: rotate(calc(360deg * var(--third-start) + 45deg + var(--donut-spacing-deg)));
          transform: rotate(calc(360deg * var(--third-start) + 45deg + var(--donut-spacing-deg)));
}

.donut__slice__fourth {
  --fourth-start: calc(var(--first) + var(--second) + var(--third));
  --fourth-check: max(calc(var(--fourth-start) - .5), 0);
  -webkit-clip-path: inset(0 calc(50% * (var(--fourth-check) / var(--fourth-check))) 0 0);
          clip-path: inset(0 calc(50% * (var(--fourth-check) / var(--fourth-check))) 0 0);
}

.donut__slice__fourth::before {
  border-top-color: rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
  -webkit-transform: rotate(calc(360deg * var(--fourth-start) + 45deg));
          transform: rotate(calc(360deg * var(--fourth-start) + 45deg));
}

.donut__slice__fourth::after {
  border-top-color: #1cb2f6;
  border-right-color: rgba(28, 178, 246, calc(100 * (var(--fourth) - .25)));
  border-bottom-color: rgba(28, 178, 246, calc(100 * (var(--fourth) - .5)));
  border-left-color: rgba(28, 178, 246, calc(100 * (var(--fourth) - .75)));
  -webkit-transform: rotate(calc(360deg * var(--fourth-start) + 45deg + var(--donut-spacing-deg)));
          transform: rotate(calc(360deg * var(--fourth-start) + 45deg + var(--donut-spacing-deg)));
}

.donut__slice__fifth {
  --fifth-start: calc(var(--first) + var(--second) + var(--third) + var(--fourth));
  --fifth-check: max(calc(var(--fifth-start) - .5), 0);
  -webkit-clip-path: inset(0 calc(50% * (var(--fifth-check) / var(--fifth-check))) 0 0);
          clip-path: inset(0 calc(50% * (var(--fifth-check) / var(--fifth-check))) 0 0);
}

.donut__slice__fifth::before {
  border-top-color: rgba(var(--donut-spacing-color), calc(100 * var(--donut-spacing)));
  -webkit-transform: rotate(calc(360deg * var(--fifth-start) + 45deg));
          transform: rotate(calc(360deg * var(--fifth-start) + 45deg));
}

.donut__slice__fifth::after {
  border-top-color: #1685b8;
  border-right-color: rgba(22, 133, 184, calc(100 * (var(--fifth) - .25)));
  border-bottom-color: rgba(22, 133, 184, calc(100 * (var(--fifth) - .5)));
  border-left-color: rgba(22, 133, 184, calc(100 * (var(--fifth) - .75)));
  -webkit-transform: rotate(calc(360deg * var(--fifth-start) + 45deg + var(--donut-spacing-deg)));
          transform: rotate(calc(360deg * var(--fifth-start) + 45deg + var(--donut-spacing-deg)));
}

You Might Be Interested In:


3 thoughts on “CSS Only Donut Chart With Custom Slices

  1. Chris

    Very helpful. A quick question: How could you add labels to each slice. Thanks!!

    Reply
  2. specko

    It has a bug when you use 3 colors and use value 0.3 / 0.6 / 0.1

    Reply
  3. Emmie

    If you place 0.6 first and the smaller numbers after, it is working. I also ran into the issue when the first slice is not the largest number it is incorrect. So it is taking away bits from the end instead of the beginning

    Reply

Leave a Reply