Circle Timer
A copy-paste countdowns component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.
CountdownsHTMLCSSJavaScriptany framework
42
Copy into your project
HTML
<div class="nuda-circle-timer" role="timer" aria-label="Timer">
<svg class="nuda-circle-timer__svg" viewBox="0 0 100 100" width="80" height="80">
<circle class="nuda-circle-timer__track" cx="50" cy="50" r="44"
fill="none" stroke="currentColor" stroke-width="4" />
<circle class="nuda-circle-timer__progress" cx="50" cy="50" r="44"
fill="none" stroke="currentColor" stroke-width="4"
stroke-linecap="round" />
</svg>
<span class="nuda-circle-timer__value">60</span>
</div>CSS
/* Circle Timer
Circular countdown with animated SVG stroke.
Customize: --timer-color, --timer-track, --timer-duration */
.nuda-circle-timer {
--timer-color: #e4ff54;
--timer-track: rgba(255, 255, 255, 0.08);
--timer-duration: 60s;
position: relative;
display: inline-flex;
align-items: center;
justify-content: center;
}
.nuda-circle-timer__svg {
transform: rotate(-90deg);
}
.nuda-circle-timer__track {
color: var(--timer-track);
}
.nuda-circle-timer__progress {
color: var(--timer-color);
/* circumference = 2 * PI * 44 = 276.46 */
stroke-dasharray: 276.46;
stroke-dashoffset: 0;
animation: nuda-circle-countdown var(--timer-duration) linear forwards;
}
.nuda-circle-timer__value {
position: absolute;
color: #fff;
font-size: 1.2rem;
font-weight: 700;
font-variant-numeric: tabular-nums;
}
@keyframes nuda-circle-countdown {
from { stroke-dashoffset: 0; }
to { stroke-dashoffset: 276.46; }
}
@media (prefers-reduced-motion: reduce) {
.nuda-circle-timer__progress {
animation: none;
}
}JavaScript
/* Circle Timer — Updates the number as the circle depletes. */
(function () {
var timer = document.querySelector('.nuda-circle-timer');
if (!timer) return;
var valueEl = timer.querySelector('.nuda-circle-timer__value');
if (!valueEl) return;
var duration = 60; // seconds
var remaining = duration;
valueEl.textContent = remaining;
var interval = setInterval(function () {
remaining--;
valueEl.textContent = Math.max(0, remaining);
if (remaining <= 0) clearInterval(interval);
}, 1000);
})();How to use Circle Timer
Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.
Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.