Pure CSS Pie Chart Visualization with dynamically placed labels

I made something kind of fun! This pie chart uses some math, conic gradients, and rotation in order to visualize data stored in an array. You could hook this up to a repeater field with a number and color field, or load in dynamic data instead of inline-defined maps.

Here's my markup
<Note>Data Setup</Note>
<Map not-started>
  <Key value>12</Key>
  <Key color>#D4D4D4</Key>
</Map>
<Map in-progress>
  <Key value>40</Key>
  <Key color>#EFBC24</Key>
</Map>
<Map complete>
  <Key value>22</Key>
  <Key color>#00966F</Key>
</Map>
<Map other>
  <Key value>12</Key>
  <Key color>red</Key>
</Map>
<Map another>
  <Key value>4</Key>
  <Key color>blue</Key>
</Map>
<Note>Loopable list with map names</Note>
<List name=pieData>
  <Item>not-started</Item>
  <Item>in-progress</Item>
  <Item>complete</Item>
  <Item>other</Item>
  <Item>another</Item>
</List>

<Note>Store calculated percentages in dynamically named math variables for later use</Note>
<Math>total = <Loop list=pieData><Field map="{Field /}" name=value /><If not last> + </If></Loop></Math>
<Loop list=pieData><Math>percentage<Get loop=count /> = <Field map="{Field /}" name=value /> / total * 100</Math> </Loop>

<Note>Create the conic gradient</Note>
<Set stop_start>0</Set><Set stop_end>0</Set>
<Set gradient>conic-gradient(<Loop list=pieData><Set stop_end><Math><Get stop_end /> + percentage<Get loop=count /></Math></Set><Field map="{Field /}" name=color /> <Get stop_start />% <If first><Math>percentage<Get loop=count /></Math>%<Else /><Get stop_end />%</If><If not last>, </If><Set stop_start><Get stop_end /></Set></Loop>);</Set>

<div class="pie-chart">
  <Note>Apply the conic gradient as an inline style</Note>
  <div class="pie-chart__chart" style="
  background: {Get gradient /}"></div>
  
  <Note>Output the labels, which need to be rotated to half the current pie slice + the already output slices, subtracting 90 degrees to account for the different rotation starting point vs the conic gradient start point. The content within the label is rotated the same amount negative so that the lables aren't askew</Note>
  <Set rotationTotal>0</Set>
  <Loop list=pieData>
    <Set dataDeg><Math>( percentage<Get loop=count /> / 100 * 360 )</Math></Set>
    <Set rotation><Math><Get rotationTotal /> + (<Get dataDeg /> * 0.5 ) - 90</Math></Set>
    <Set rotation_neg><Math><Get rotation /> * -1</Math></Set>
      <span class="pie-chart__label pie-chart__label--{Field /}" style="rotate: {Get rotation /}deg;"><span class="pie-chart__label__inner" style="rotate: {Get rotation_neg /}deg;"><Format number decimals=0><Math>percentage<Get loop=count /></Math></Format>%</span></span>
    <Set rotationTotal><Math><Get rotationTotal /> + <Get dataDeg /></Math></Set>
  </Loop>
</div>
And my styling
.pie-chart {
  position: relative;
  width:250px;
  height:250px;
  display:inline-block;
}
.pie-chart__chart {
  width: 100%;
  height: 100%;
  border-radius: 50%;
}
.pie-chart__label {
  position: absolute;
  transform-origin: 50% 50%;
  top: calc(50% - 1.25em); /* Needs to be calculated relative to label height */
  left: -0.75em; /* adjust according to label width to properly offset */
  right: -0.75em; /* adjust according to label width to properly offset */
  text-align: right;
}

.pie-chart__label__inner {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 100%;
  width: 2.5em;
  height: 2.5em;
  box-shadow: 0px 0px 17px 0px rgba(0, 0, 0, 0.16);
  transform-origin: 50% 50%;
  background: #fff;
}

And a little preview:

3 Likes