Skip to content

Progress Ring Countdown

--sv-progress-ring-clockwise: -1 reverses the fill direction, and a linear --sv-progress-ring-ease with a one second --sv-progress-ring-dur makes each tick blend into the next. The data-value-text child keeps aria-valuetext in sync with the formatted time.

1:00

Break time

Code

The following example assumes that you have imported the component and set up the theme.

Countdown
<div class="countdown-card">
7 collapsed lines
<sv-progress-ring aria-label="Time remaining" max="60" value="60">
<p class="countdown-time" data-value-text>
<sv-text-flow>1:00</sv-text-flow>
</p>
</sv-progress-ring>
<p class="countdown-title">Break time</p>
</div>
<script type="application/x-typescript" module>
19 collapsed lines
const card = document.querySelector<HTMLElement>(".countdown-card")!;
const ring = card.querySelector<ProgressRing>("sv-progress-ring")!;
const time = card.querySelector<TextFlow>("sv-text-flow")!;
function tick() {
const current = ring.value ?? 0;
const next = current === 0 ? 60 : current - 1;
ring.value = next;
ring.classList.toggle("urgent", next <= 10);
const minutes = Math.floor(next / 60);
const seconds = String(next % 60).padStart(2, "0");
time.textContent = `${minutes}:${seconds}`;
setTimeout(tick, next === 0 ? 2000 : 1000);
}
setTimeout(tick, 1000);
</script>
<style>
43 collapsed lines
.countdown-card {
display: grid;
gap: 1rem;
justify-items: center;
max-inline-size: 22rem;
padding: clamp(1rem, 4vw, 2rem);
margin: auto;
background-color: var(--sv-gray-7);
border: 1px solid var(--sv-gray-5);
border-radius: var(--sv-bdr-rad-lg);
sv-progress-ring {
--sv-progress-ring-clockwise: -1;
--sv-progress-ring-dur: 1s;
--sv-progress-ring-ease: linear;
--sv-progress-ring-track-thickness: 3px;
--sv-progress-ring-fill-thickness: 6px;
inline-size: 8rem;
transition: color 300ms ease;
&.urgent {
--sv-progress-ring-fill-clr: #ef4444;
color: #ef4444;
}
}
.countdown-time {
margin: 0;
sv-text-flow::part(text) {
font-family: monospace;
font-size: 1.6rem;
}
}
.countdown-title {
margin: 0;
font-size: 0.85rem;
color: var(--sv-gray-3);
}
}
</style>