Skip to content

Progress Ring Examples

The examples below assume that you have imported the component and set up the theme.

Basic

Set value within the min/max range, and the fill animates to every assigned value. The ring fills the element, so give it a size.

<div class="ring-basic">
<sv-progress-ring aria-label="Upload progress" value="65"></sv-progress-ring>
</div>
<style>
.ring-basic {
display: flex;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
inline-size: 6rem;
}
}
</style>

Indeterminate

Without a value, the ring is indeterminate and spins until one is assigned.

Reduced Motion

Your system is set to reduce motion, so a short arc sits still at the top of the ring.

<div class="ring-indeterminate">
<sv-progress-ring aria-label="Loading"></sv-progress-ring>
</div>
<style>
.ring-indeterminate {
display: flex;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
inline-size: 3rem;
}
}
</style>

Content

Slotted content is centered inside the ring. A child with data-percentage or data-value renders the current number, and the attribute's number sets the decimal places. A data-value-text element supplies aria-valuetext, since slotted content is hidden from assistive technology.

%

of 8

<div class="ring-content">
<sv-progress-ring aria-label="Storage used" value="81.5">
<p data-value-text>
<span data-percentage="1"></span>
<span>%</span>
</p>
</sv-progress-ring>
<sv-progress-ring aria-label="Downloaded episodes" max="8" value="3">
<p data-value-text>
<span data-value></span>
of 8
</p>
</sv-progress-ring>
</div>
<style>
.ring-content {
display: flex;
flex-wrap: wrap;
gap: 2em;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
inline-size: 6rem;
}
p {
margin: 0;
font-size: 0.875rem;
}
}
</style>

Download

Assign value from JavaScript whenever progress arrives, and the fill animates to it on its own.

%

<div class="ring-download">
<sv-progress-ring class="download-ring" aria-label="Download progress" value="0">
<p data-value-text>
<span data-percentage="0"></span>
<span>%</span>
</p>
</sv-progress-ring>
</div>
<script type="application/x-typescript" module>
const ring = document.querySelector<ProgressRing>(".download-ring")!;
let speed = 100;
function tick() {
const current = ring.value ?? 0;
const next = current >= ring.max ? 0 : current + 1;
ring.value = next;
if (Math.random() < 0.1) {
speed = 20 + Math.random() * 380;
}
setTimeout(tick, next >= ring.max ? 1500 : speed);
}
tick();
</script>
<style>
.ring-download {
display: flex;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
inline-size: 6rem;
}
p {
margin: 0;
font-size: 0.875rem;
}
}
</style>

Styling

Custom properties control the thickness, line cap, fill direction, and colors of the ring.

<div class="ring-styling">
<sv-progress-ring class="thick" aria-label="Thick ring" value="65"></sv-progress-ring>
<sv-progress-ring class="counter" aria-label="Counter-clockwise ring" value="65"></sv-progress-ring>
<sv-progress-ring class="tinted" aria-label="Tinted ring" value="65"></sv-progress-ring>
</div>
<style>
.ring-styling {
display: flex;
flex-wrap: wrap;
gap: 2em;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
inline-size: 6rem;
}
.thick {
--sv-progress-ring-track-thickness: 8px;
--sv-progress-ring-fill-thickness: 12px;
--sv-progress-ring-fill-linecap: butt;
}
.counter {
--sv-progress-ring-clockwise: -1;
}
.tinted {
--sv-progress-ring-fill-clr: #22c55e;
--sv-progress-ring-track-clr: color-mix(in srgb, #22c55e 25%, transparent);
--sv-progress-ring-bg: color-mix(in srgb, #22c55e 10%, transparent);
}
}
</style>

Dashed

Split the fill into dashes with --sv-progress-ring-dash-num, and set the space between them with --sv-progress-ring-dash-gap.

<div class="ring-dashed">
<sv-progress-ring class="few-dashes" aria-label="Ring with 12 dashes" value="65"></sv-progress-ring>
<sv-progress-ring class="many-dashes" aria-label="Ring with 40 dashes" value="65"></sv-progress-ring>
<sv-progress-ring class="many-dashes" aria-label="Loading"></sv-progress-ring>
</div>
<style>
.ring-dashed {
display: flex;
flex-wrap: wrap;
gap: 2em;
justify-content: center;
padding-block: 1em;
sv-progress-ring {
--sv-progress-ring-track-thickness: 12px;
--sv-progress-ring-fill-thickness: 12px;
--sv-progress-ring-fill-linecap: butt;
inline-size: 6rem;
}
.few-dashes {
--sv-progress-ring-dash-num: 12;
--sv-progress-ring-dash-gap: 6px;
}
.many-dashes {
--sv-progress-ring-dash-num: 40;
--sv-progress-ring-dash-gap: 3px;
}
}
</style>