@use 'sprucecss/scss/spruce' as *;
.donut-chart {
--donut-chart-size: 11rem;
--donut-chart-thickness: 1.5rem;
--donut-chart-color-1: #{color('primary')};
--donut-chart-color-2: #{color('info', 'alert')};
--donut-chart-color-3: #{color('warning', 'alert')};
--donut-chart-color-4: #{color('success', 'alert')};
background-color: color('surface');
border: config('border-width-base', $display) solid color('border');
border-radius: radius('lg');
margin: 0;
padding: spacer('m');
@include layout-stack('m');
&__caption {
display: flex;
flex-wrap: wrap;
gap: spacer('xxs') spacer('xs');
justify-content: space-between;
}
&__title {
color: color('heading');
font-weight: 700;
}
&__subtitle {
color: color('muted');
font-size: config('font-size-sm', $typography);
}
&__body {
align-items: center;
display: flex;
flex-wrap: wrap;
gap: spacer('m') spacer('l');
justify-content: center;
}
&__ring {
align-items: center;
aspect-ratio: 1;
display: grid;
flex-shrink: 0;
inline-size: var(--donut-chart-size);
justify-items: center;
position: relative;
&::before {
background: conic-gradient(
var(--donut-chart-color-1) 0 var(--segment-1),
var(--donut-chart-color-2) 0 var(--segment-2),
var(--donut-chart-color-3) 0 var(--segment-3),
var(--donut-chart-color-4) 0
);
border-radius: 50%;
content: '';
inset: 0;
mask: radial-gradient(farthest-side, transparent calc(100% - var(--donut-chart-thickness)), #000 calc(100% - var(--donut-chart-thickness) + 1px));
position: absolute;
@include motion-safe {
animation: donut-chart-in 0.8s ease-out;
}
}
}
&__total {
color: color('muted');
display: grid;
font-size: config('font-size-sm', $typography);
line-height: 1.3;
margin: 0;
text-align: center;
strong {
color: color('heading');
font-family: config('font-family-heading', $typography);
font-size: font-size('h3');
}
}
&__legend {
@include clear-list;
display: grid;
flex-grow: 1;
gap: spacer('xs');
max-inline-size: 18rem;
> li {
margin-block: 0;
padding-block: 0;
}
}
&__key {
align-items: center;
display: flex;
gap: spacer('xs');
@for $i from 1 through 4 {
&:nth-child(#{$i}) {
--donut-chart-key-color: var(--donut-chart-color-#{$i});
}
}
}
&__swatch {
background-color: var(--donut-chart-key-color);
block-size: 0.75rem;
border-radius: radius('sm');
flex-shrink: 0;
inline-size: 0.75rem;
}
&__label {
flex-grow: 1;
}
&__value {
color: color('heading');
font-variant-numeric: tabular-nums;
font-weight: 700;
}
}
@keyframes donut-chart-in {
from {
opacity: 0;
transform: rotate(-90deg) scale(0.8);
}
}
<figure class="donut-chart">
<figcaption class="donut-chart__caption">
<span class="donut-chart__title">Traffic sources</span>
<span class="donut-chart__subtitle">Last 30 days</span>
</figcaption>
<div class="donut-chart__body">
<div class="donut-chart__ring" style="--segment-1: 42%; --segment-2: 70%; --segment-3: 88%;">
<p class="donut-chart__total"><strong>24.8k</strong> visits</p>
</div>
<ul class="donut-chart__legend" role="list">
<li class="donut-chart__key"><span class="donut-chart__swatch" aria-hidden="true"></span><span class="donut-chart__label">Organic search</span><span class="donut-chart__value">42%</span></li>
<li class="donut-chart__key"><span class="donut-chart__swatch" aria-hidden="true"></span><span class="donut-chart__label">Direct</span><span class="donut-chart__value">28%</span></li>
<li class="donut-chart__key"><span class="donut-chart__swatch" aria-hidden="true"></span><span class="donut-chart__label">Social</span><span class="donut-chart__value">18%</span></li>
<li class="donut-chart__key"><span class="donut-chart__swatch" aria-hidden="true"></span><span class="donut-chart__label">Referral</span><span class="donut-chart__value">12%</span></li>
</ul>
</div>
</figure>