Open demo

CSS code

HTML
<div><span class="lbl">Conic-gradient rings</span><div class="card">
<div class="cp cp-rings">
  <div class="cp-item"><div class="cp-ring" role="progressbar" aria-labelledby="cp-storage" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" style="--v:72"><b aria-hidden="true">72%</b></div><span id="cp-storage">Storage</span></div>
  <div class="cp-item"><div class="cp-ring" role="progressbar" aria-labelledby="cp-tasks" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="--v:45;--c:#0284c7"><b aria-hidden="true">45%</b></div><span id="cp-tasks">Sprint tasks</span></div>
  <div class="cp-item"><div class="cp-ring" role="progressbar" aria-labelledby="cp-budget" aria-valuenow="88" aria-valuemin="0" aria-valuemax="100" style="--v:88;--c:#e11d48"><b aria-hidden="true">88%</b></div><span id="cp-budget">Ad budget</span></div>
</div></div></div>

<div><span class="lbl">SVG ring with round caps</span><div class="card">
<div class="cp">
  <div class="cp-svg" role="progressbar" aria-labelledby="cp-course" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" aria-valuetext="70%, 7 of 10 lessons" style="--v:70">
    <svg viewBox="0 0 120 120" aria-hidden="true"><circle class="cp-t" cx="60" cy="60" r="52"/><circle class="cp-v" cx="60" cy="60" r="52" pathLength="100"/></svg>
    <div aria-hidden="true"><b>70%</b><span>7 of 10 lessons</span></div>
  </div>
  <p class="cp-cap" id="cp-course">Spanish for Travellers</p>
</div></div></div>

<div><span class="lbl">Half gauge</span><div class="card">
<div class="cp">
  <div class="cp-gauge" role="progressbar" aria-labelledby="cp-fund" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100" aria-valuetext="$6,400 of $10,000 raised, 64%" style="--v:64">
    <div aria-hidden="true"><b>64%</b><span>$6,400 of $10,000</span></div>
  </div>
  <div class="cp-ends" aria-hidden="true"><span>$0</span><span>$10k</span></div>
  <p class="cp-cap" id="cp-fund">Library roof fundraiser</p>
</div></div></div>

<div><span class="lbl">Small donuts in a list</span><div class="card">
<ul class="cp cp-list">
  <li><span class="cp-donut" role="progressbar" aria-labelledby="cp-home" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="--v:80"></span><b id="cp-home">Homepage redesign</b><span aria-hidden="true">80%</span></li>
  <li><span class="cp-donut" role="progressbar" aria-labelledby="cp-docs" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="--v:45;--c:#0284c7"></span><b id="cp-docs">API reference</b><span aria-hidden="true">45%</span></li>
  <li><span class="cp-donut" role="progressbar" aria-labelledby="cp-qa" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="--v:20;--c:#e11d48"></span><b id="cp-qa">Mobile QA pass</b><span aria-hidden="true">20%</span></li>
  <li><span class="cp-donut" role="progressbar" aria-labelledby="cp-mail" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-valuetext="100%, done" style="--v:100;--c:#059669"></span><b id="cp-mail">Launch email</b><span aria-hidden="true">Done</span></li>
</ul></div></div>
CSS
/* Circular progress. Every ring is an element with role="progressbar" and --v (0 to 100).
   Conic rings, the half gauge and the donuts paint a conic-gradient and cut the hole with a radial mask;
   the SVG ring draws a circle with pathLength="100", so stroke-dasharray takes the value as it is. */
.cp{--cp-track:#e3e7ef;--cp-ink:#1c2333;--cp-muted:#56607a}
/* conic ring */
.cp-rings{display:flex;flex-wrap:wrap;justify-content:space-around;gap:20px}
.cp-item{display:grid;justify-items:center;gap:10px;font-size:.86rem;color:var(--cp-muted)}
.cp-item>span:last-child{font-weight:600;color:var(--cp-ink)}
.cp-ring{--v:0;--c:#7c3aed;--size:92px;--thick:10px;position:relative;display:grid;place-items:center;width:var(--size);height:var(--size)}
.cp-ring::before{content:"";position:absolute;inset:0;border-radius:50%;
  background:conic-gradient(var(--c) calc(var(--v) * 1%),var(--cp-track) 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - var(--thick)),#000 calc(100% - var(--thick) + 1px));
  mask:radial-gradient(farthest-side,transparent calc(100% - var(--thick)),#000 calc(100% - var(--thick) + 1px))}
.cp-ring b{position:relative;font-size:1.25rem;color:var(--cp-ink);font-variant-numeric:tabular-nums}
/* SVG ring with round caps */
.cp-svg{--v:0;--c:#059669;position:relative;display:grid;place-items:center;width:156px;height:156px;margin:0 auto}
.cp-svg svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.cp-svg circle{fill:none;stroke-width:10}
.cp-svg .cp-t{stroke:var(--cp-track)}
.cp-svg .cp-v{stroke:var(--c);stroke-linecap:round;stroke-dasharray:var(--v) 100;transition:stroke-dasharray .6s ease}
.cp-svg div{position:relative;text-align:center}
.cp-svg b{display:block;font-size:2rem;letter-spacing:-.02em;color:var(--cp-ink);font-variant-numeric:tabular-nums}
.cp-svg span{font-size:.8rem;color:var(--cp-muted)}
.cp-cap{margin-top:14px;text-align:center;font-size:.92rem;font-weight:600;color:var(--cp-ink)}
/* half gauge: a full conic circle, clipped to its top half */
.cp-gauge{--v:0;--c:#0369a1;position:relative;width:min(240px,100%);aspect-ratio:2/1;margin:0 auto;overflow:hidden}
.cp-gauge::before{content:"";position:absolute;inset:0 0 -100%;border-radius:50%;
  background:conic-gradient(from -90deg,var(--c) calc(var(--v) * .5%),var(--cp-track) 0 50%,transparent 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 20px),#000 calc(100% - 19px));
  mask:radial-gradient(farthest-side,transparent calc(100% - 20px),#000 calc(100% - 19px))}
.cp-gauge div{position:absolute;inset:auto 0 0;text-align:center}
.cp-gauge b{display:block;font-size:1.8rem;line-height:1.1;color:var(--cp-ink);font-variant-numeric:tabular-nums}
.cp-gauge span{font-size:.8rem;color:var(--cp-muted)}
.cp-ends{display:flex;justify-content:space-between;width:min(240px,100%);margin:6px auto 0;font-size:.78rem;color:var(--cp-muted)}
/* small donuts in a list */
.cp-list{display:grid;list-style:none}
.cp-list li{display:flex;align-items:center;gap:12px;padding:11px 0;font-size:.9rem;border-top:1px solid #edf0f5}
.cp-list li:first-child{border-top:0;padding-top:0}
.cp-list li:last-child{padding-bottom:0}
.cp-list li b{flex:1;font-weight:600;color:var(--cp-ink)}
.cp-list li>span:last-child{font-size:.84rem;font-weight:700;color:var(--cp-muted);font-variant-numeric:tabular-nums}
.cp-donut{--v:0;--c:#7c3aed;flex:none;width:26px;height:26px;border-radius:50%;
  background:conic-gradient(var(--c) calc(var(--v) * 1%),var(--cp-track) 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 5px),#000 calc(100% - 4px));
  mask:radial-gradient(farthest-side,transparent calc(100% - 5px),#000 calc(100% - 4px))}
@media (prefers-reduced-motion:reduce){.cp-svg .cp-v{transition:none}}

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Conic-gradient rings</span><div class="card demo-card"><div class="card-body p-4">
<div class="cp cp-rings d-flex flex-wrap justify-content-around gap-4">
  <div class="cp-item d-flex flex-column align-items-center gap-2 text-center"><div class="cp-ring" role="progressbar" aria-labelledby="cp-storage" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100" style="--v:72"><b aria-hidden="true">72%</b></div><span id="cp-storage">Storage</span></div>
  <div class="cp-item d-flex flex-column align-items-center gap-2 text-center"><div class="cp-ring" role="progressbar" aria-labelledby="cp-tasks" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="--v:45;--c:#0284c7"><b aria-hidden="true">45%</b></div><span id="cp-tasks">Sprint tasks</span></div>
  <div class="cp-item d-flex flex-column align-items-center gap-2 text-center"><div class="cp-ring" role="progressbar" aria-labelledby="cp-budget" aria-valuenow="88" aria-valuemin="0" aria-valuemax="100" style="--v:88;--c:#e11d48"><b aria-hidden="true">88%</b></div><span id="cp-budget">Ad budget</span></div>
</div></div></div></div>

<div><span class="lbl">SVG ring with round caps</span><div class="card demo-card"><div class="card-body p-4">
<div class="cp">
  <div class="cp-svg" role="progressbar" aria-labelledby="cp-course" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" aria-valuetext="70%, 7 of 10 lessons" style="--v:70">
    <svg viewBox="0 0 120 120" aria-hidden="true"><circle class="cp-t" cx="60" cy="60" r="52"/><circle class="cp-v" cx="60" cy="60" r="52" pathLength="100"/></svg>
    <div aria-hidden="true"><b>70%</b><span>7 of 10 lessons</span></div>
  </div>
  <p class="cp-cap mt-3 mb-0 text-center fw-semibold" id="cp-course">Spanish for Travellers</p>
</div></div></div></div>

<div><span class="lbl">Half gauge</span><div class="card demo-card"><div class="card-body p-4">
<div class="cp">
  <div class="cp-gauge" role="progressbar" aria-labelledby="cp-fund" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100" aria-valuetext="$6,400 of $10,000 raised, 64%" style="--v:64">
    <div aria-hidden="true"><b>64%</b><span>$6,400 of $10,000</span></div>
  </div>
  <div class="cp-ends" aria-hidden="true"><span>$0</span><span>$10k</span></div>
  <p class="cp-cap mt-3 mb-0 text-center fw-semibold" id="cp-fund">Library roof fundraiser</p>
</div></div></div></div>

<div><span class="lbl">Small donuts in a list</span><div class="card demo-card"><div class="card-body p-4">
<ul class="cp cp-list list-unstyled mb-0">
  <li class="d-flex align-items-center gap-3"><span class="cp-donut" role="progressbar" aria-labelledby="cp-home" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100" style="--v:80"></span><b class="flex-grow-1 fw-semibold" id="cp-home">Homepage redesign</b><span aria-hidden="true">80%</span></li>
  <li class="d-flex align-items-center gap-3"><span class="cp-donut" role="progressbar" aria-labelledby="cp-docs" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100" style="--v:45;--c:#0284c7"></span><b class="flex-grow-1 fw-semibold" id="cp-docs">API reference</b><span aria-hidden="true">45%</span></li>
  <li class="d-flex align-items-center gap-3"><span class="cp-donut" role="progressbar" aria-labelledby="cp-qa" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="--v:20;--c:#e11d48"></span><b class="flex-grow-1 fw-semibold" id="cp-qa">Mobile QA pass</b><span aria-hidden="true">20%</span></li>
  <li class="d-flex align-items-center gap-3"><span class="cp-donut" role="progressbar" aria-labelledby="cp-mail" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-valuetext="100%, done" style="--v:100;--c:#059669"></span><b class="flex-grow-1 fw-semibold" id="cp-mail">Launch email</b><span aria-hidden="true">Done</span></li>
</ul></div></div></div>
CSS
/* Circular progress for Bootstrap 5. Bootstrap has no ring component, so the rings are this small stylesheet and the
   layout around them is Bootstrap utilities. Every ring is an element with role="progressbar" and --v (0 to 100).
   Conic rings, the half gauge and the donuts paint a conic-gradient and cut the hole with a radial mask;
   the SVG ring draws a circle with pathLength="100", so stroke-dasharray takes the value as it is. */
.cp{--cp-track:#e3e7ef;--cp-ink:#1c2333;--cp-muted:#56607a}
/* conic ring */
.cp-item{font-size:.86rem;color:var(--cp-muted)}
.cp-item>span:last-child{font-weight:600;color:var(--cp-ink)}
.cp-ring{--v:0;--c:#7c3aed;--size:92px;--thick:10px;position:relative;display:grid;place-items:center;width:var(--size);height:var(--size)}
.cp-ring::before{content:"";position:absolute;inset:0;border-radius:50%;
  background:conic-gradient(var(--c) calc(var(--v) * 1%),var(--cp-track) 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - var(--thick)),#000 calc(100% - var(--thick) + 1px));
  mask:radial-gradient(farthest-side,transparent calc(100% - var(--thick)),#000 calc(100% - var(--thick) + 1px))}
.cp-ring b{position:relative;font-size:1.25rem;color:var(--cp-ink);font-variant-numeric:tabular-nums}
/* SVG ring with round caps */
.cp-svg{--v:0;--c:#059669;position:relative;display:grid;place-items:center;width:156px;height:156px;margin:0 auto}
.cp-svg svg{position:absolute;inset:0;width:100%;height:100%;transform:rotate(-90deg)}
.cp-svg circle{fill:none;stroke-width:10}
.cp-svg .cp-t{stroke:var(--cp-track)}
.cp-svg .cp-v{stroke:var(--c);stroke-linecap:round;stroke-dasharray:var(--v) 100;transition:stroke-dasharray .6s ease}
.cp-svg div{position:relative;text-align:center}
.cp-svg b{display:block;font-size:2rem;letter-spacing:-.02em;color:var(--cp-ink);font-variant-numeric:tabular-nums}
.cp-svg span{font-size:.8rem;color:var(--cp-muted)}
.cp-cap{font-size:.92rem;color:var(--cp-ink)}
/* half gauge: a full conic circle, clipped to its top half */
.cp-gauge{--v:0;--c:#0369a1;position:relative;width:min(240px,100%);aspect-ratio:2/1;margin:0 auto;overflow:hidden}
.cp-gauge::before{content:"";position:absolute;inset:0 0 -100%;border-radius:50%;
  background:conic-gradient(from -90deg,var(--c) calc(var(--v) * .5%),var(--cp-track) 0 50%,transparent 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 20px),#000 calc(100% - 19px));
  mask:radial-gradient(farthest-side,transparent calc(100% - 20px),#000 calc(100% - 19px))}
.cp-gauge div{position:absolute;inset:auto 0 0;text-align:center}
.cp-gauge b{display:block;font-size:1.8rem;line-height:1.1;color:var(--cp-ink);font-variant-numeric:tabular-nums}
.cp-gauge span{font-size:.8rem;color:var(--cp-muted)}
.cp-ends{display:flex;justify-content:space-between;width:min(240px,100%);margin:6px auto 0;font-size:.78rem;color:var(--cp-muted)}
/* small donuts in a list */
.cp-list li{padding:11px 0;font-size:.9rem;border-top:1px solid #edf0f5}
.cp-list li:first-child{border-top:0;padding-top:0}
.cp-list li:last-child{padding-bottom:0}
.cp-list li b{color:var(--cp-ink)}
.cp-list li>span:last-child{font-size:.84rem;font-weight:700;color:var(--cp-muted);font-variant-numeric:tabular-nums}
.cp-donut{--v:0;--c:#7c3aed;flex:none;width:26px;height:26px;border-radius:50%;
  background:conic-gradient(var(--c) calc(var(--v) * 1%),var(--cp-track) 0);
  -webkit-mask:radial-gradient(farthest-side,transparent calc(100% - 5px),#000 calc(100% - 4px));
  mask:radial-gradient(farthest-side,transparent calc(100% - 5px),#000 calc(100% - 4px))}
@media (prefers-reduced-motion:reduce){.cp-svg .cp-v{transition:none}}

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-cp-muted uppercase">Conic-gradient rings</span><div class="rounded-2xl border border-[#e1e6ef] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(28,35,51,.04)]">
<div class="flex flex-wrap justify-around gap-5">
  <div class="grid justify-items-center gap-2.5 text-[.86rem]"><div class="relative grid size-[92px] place-items-center before:absolute before:inset-0 before:rounded-full before:bg-[conic-gradient(var(--color-cp-violet)_72%,var(--color-cp-track)_0)] before:[mask:radial-gradient(farthest-side,transparent_calc(100%_-_10px),#000_calc(100%_-_9px))]" role="progressbar" aria-labelledby="cp-storage" aria-valuenow="72" aria-valuemin="0" aria-valuemax="100"><b class="relative text-[1.25rem] tabular-nums" aria-hidden="true">72%</b></div><span class="font-semibold" id="cp-storage">Storage</span></div>
  <div class="grid justify-items-center gap-2.5 text-[.86rem]"><div class="relative grid size-[92px] place-items-center before:absolute before:inset-0 before:rounded-full before:bg-[conic-gradient(var(--color-cp-sky)_45%,var(--color-cp-track)_0)] before:[mask:radial-gradient(farthest-side,transparent_calc(100%_-_10px),#000_calc(100%_-_9px))]" role="progressbar" aria-labelledby="cp-tasks" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100"><b class="relative text-[1.25rem] tabular-nums" aria-hidden="true">45%</b></div><span class="font-semibold" id="cp-tasks">Sprint tasks</span></div>
  <div class="grid justify-items-center gap-2.5 text-[.86rem]"><div class="relative grid size-[92px] place-items-center before:absolute before:inset-0 before:rounded-full before:bg-[conic-gradient(var(--color-cp-rose)_88%,var(--color-cp-track)_0)] before:[mask:radial-gradient(farthest-side,transparent_calc(100%_-_10px),#000_calc(100%_-_9px))]" role="progressbar" aria-labelledby="cp-budget" aria-valuenow="88" aria-valuemin="0" aria-valuemax="100"><b class="relative text-[1.25rem] tabular-nums" aria-hidden="true">88%</b></div><span class="font-semibold" id="cp-budget">Ad budget</span></div>
</div></div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-cp-muted uppercase">SVG ring with round caps</span><div class="rounded-2xl border border-[#e1e6ef] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(28,35,51,.04)]">
<div>
  <div class="relative mx-auto grid size-[156px] place-items-center" role="progressbar" aria-labelledby="cp-course" aria-valuenow="70" aria-valuemin="0" aria-valuemax="100" aria-valuetext="70%, 7 of 10 lessons">
    <svg class="absolute inset-0 size-full -rotate-90" viewBox="0 0 120 120" aria-hidden="true"><circle class="fill-none stroke-cp-track stroke-10" cx="60" cy="60" r="52"/><circle class="fill-none stroke-cp-green stroke-10 [stroke-dasharray:70_100] [stroke-linecap:round] transition-[stroke-dasharray] duration-[600ms] motion-reduce:transition-none" cx="60" cy="60" r="52" pathLength="100"/></svg>
    <div class="relative text-center" aria-hidden="true"><b class="block text-[2rem] tracking-[-.02em] tabular-nums">70%</b><span class="text-[.8rem] text-cp-muted">7 of 10 lessons</span></div>
  </div>
  <p class="mt-3.5 text-center text-[.92rem] font-semibold" id="cp-course">Spanish for Travellers</p>
</div></div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-cp-muted uppercase">Half gauge</span><div class="rounded-2xl border border-[#e1e6ef] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(28,35,51,.04)]">
<div>
  <div class="relative mx-auto aspect-[2/1] w-[min(240px,100%)] overflow-hidden before:absolute before:inset-[0_0_-100%] before:rounded-full before:bg-[conic-gradient(from_-90deg,var(--color-cp-blue)_32%,var(--color-cp-track)_0_50%,transparent_0)] before:[mask:radial-gradient(farthest-side,transparent_calc(100%_-_20px),#000_calc(100%_-_19px))]" role="progressbar" aria-labelledby="cp-fund" aria-valuenow="64" aria-valuemin="0" aria-valuemax="100" aria-valuetext="$6,400 of $10,000 raised, 64%">
    <div class="absolute inset-x-0 bottom-0 text-center" aria-hidden="true"><b class="block text-[1.8rem] leading-[1.1] tabular-nums">64%</b><span class="text-[.8rem] text-cp-muted">$6,400 of $10,000</span></div>
  </div>
  <div class="mx-auto mt-1.5 flex w-[min(240px,100%)] justify-between text-[.78rem] text-cp-muted" aria-hidden="true"><span>$0</span><span>$10k</span></div>
  <p class="mt-3.5 text-center text-[.92rem] font-semibold" id="cp-fund">Library roof fundraiser</p>
</div></div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-cp-muted uppercase">Small donuts in a list</span><div class="rounded-2xl border border-[#e1e6ef] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(28,35,51,.04)]">
<ul class="grid text-[.9rem]">
  <li class="flex items-center gap-3 pb-[11px]"><span class="size-[26px] flex-none rounded-full bg-[conic-gradient(var(--color-cp-violet)_80%,var(--color-cp-track)_0)] [mask:radial-gradient(farthest-side,transparent_calc(100%_-_5px),#000_calc(100%_-_4px))]" role="progressbar" aria-labelledby="cp-home" aria-valuenow="80" aria-valuemin="0" aria-valuemax="100"></span><b class="flex-1 font-semibold" id="cp-home">Homepage redesign</b><span class="text-[.84rem] font-bold text-cp-muted tabular-nums" aria-hidden="true">80%</span></li>
  <li class="flex items-center gap-3 border-t border-[#edf0f5] py-[11px]"><span class="size-[26px] flex-none rounded-full bg-[conic-gradient(var(--color-cp-sky)_45%,var(--color-cp-track)_0)] [mask:radial-gradient(farthest-side,transparent_calc(100%_-_5px),#000_calc(100%_-_4px))]" role="progressbar" aria-labelledby="cp-docs" aria-valuenow="45" aria-valuemin="0" aria-valuemax="100"></span><b class="flex-1 font-semibold" id="cp-docs">API reference</b><span class="text-[.84rem] font-bold text-cp-muted tabular-nums" aria-hidden="true">45%</span></li>
  <li class="flex items-center gap-3 border-t border-[#edf0f5] py-[11px]"><span class="size-[26px] flex-none rounded-full bg-[conic-gradient(var(--color-cp-rose)_20%,var(--color-cp-track)_0)] [mask:radial-gradient(farthest-side,transparent_calc(100%_-_5px),#000_calc(100%_-_4px))]" role="progressbar" aria-labelledby="cp-qa" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100"></span><b class="flex-1 font-semibold" id="cp-qa">Mobile QA pass</b><span class="text-[.84rem] font-bold text-cp-muted tabular-nums" aria-hidden="true">20%</span></li>
  <li class="flex items-center gap-3 border-t border-[#edf0f5] pt-[11px]"><span class="size-[26px] flex-none rounded-full bg-[conic-gradient(var(--color-cp-green)_100%,var(--color-cp-track)_0)] [mask:radial-gradient(farthest-side,transparent_calc(100%_-_5px),#000_calc(100%_-_4px))]" role="progressbar" aria-labelledby="cp-mail" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100" aria-valuetext="100%, done"></span><b class="flex-1 font-semibold" id="cp-mail">Launch email</b><span class="text-[.84rem] font-bold text-cp-muted tabular-nums" aria-hidden="true">Done</span></li>
</ul></div></div>
CSS
@theme {
  --color-cp-ink: #1c2333;
  --color-cp-muted: #56607a;
  --color-cp-track: #e3e7ef;
  --color-cp-violet: #7c3aed;
  --color-cp-sky: #0284c7;
  --color-cp-blue: #0369a1;
  --color-cp-rose: #e11d48;
  --color-cp-green: #059669;
}

About this set

Round progress indicators built two ways. Conic-gradient rings are three 92 pixel rings for storage, sprint tasks and ad budget: a conic-gradient paints the value, a radial-gradient mask cuts the hole, and the percentage sits in the middle, so each ring is one element and one custom property. SVG ring with round caps is a larger course ring made from two circles; the value circle has pathLength=”100″, which lets stroke-dasharray take the percentage as it is, and stroke-linecap: round gives the arc soft ends that a conic-gradient cannot. Half gauge clips a full conic circle to its top half for a fundraiser at 64 percent, with the amount inside and the scale ends below. Small donuts in a list are 26 pixel rings beside task names, for project lists and tables where a bar would take too much room. Use conic rings for the lightest markup and the SVG ring when you want round caps or a stroke transition. Every ring is a role=”progressbar” element with the value in –v, and the numbers in the middle are hidden from screen readers because the ring already exposes them.

What’s included

  • Conic-gradient rings with a radial mask
  • SVG ring with round caps using pathLength="100"
  • Half gauge from a clipped conic circle
  • Small donuts for lists and tables
  • One --v custom property per ring, no JavaScript
  • Same rings in plain CSS, Bootstrap and Tailwind

Accessibility

Every ring, gauge and donut is a role="progressbar" element with aria-valuenow, aria-valuemin and aria-valuemax, named by the visible caption through aria-labelledby, with aria-valuetext where the number needs context, such as 7 of 10 lessons. The text inside the rings is aria-hidden so it is not read twice. Ring colours are at least 3:1 against the white card, and the SVG ring's stroke transition is switched off under prefers-reduced-motion.

Customise it

Change --c for the colour, --size and --thick on .cp-ring, and --cp-track on .cp, in the plain CSS and Bootstrap versions; the SVG ring takes stroke-width on its circles. In Tailwind edit the cp- theme colours and the percentage in each conic-gradient class.