Sticky Progress Ring
A copy-paste scroll-driven component in pure HTML & CSS. Zero dependencies, framework-agnostic, MIT-licensed.
Scroll-DrivenHTMLCSSany framework
Article paragraph one.
Article paragraph two.
Article paragraph three.
Article paragraph four.
Article paragraph five.
Copy into your project
HTML
<!-- Ring stroke-dashoffset unwinds with scroll progress, pinned in a
sticky corner over flowing article text -->
<div class="nuda-sd2-ring__badge">
<svg class="nuda-sd2-ring__svg" viewBox="0 0 44 44">
<circle class="nuda-sd2-ring__track" cx="22" cy="22" r="18" />
<circle class="nuda-sd2-ring__bar" cx="22" cy="22" r="18" />
</svg>
</div>
<div class="nuda-sd2-ring__content">
<p>Article paragraph one.</p>
<p>Article paragraph two.</p>
</div>CSS
.nuda-sd2-ring {
height: 160px;
border: 1px solid rgba(255,255,255,.08);
border-radius: 10px;
overflow: hidden;
background: #09090b;
}
.nuda-sd2-ring__scroller {
position: relative;
height: 100%;
overflow: auto;
padding: .8rem;
}
.nuda-sd2-ring__badge {
position: sticky;
top: .4rem;
float: right;
width: 34px;
height: 34px;
margin: 0 0 .5rem .5rem;
}
.nuda-sd2-ring__svg {
width: 100%;
height: 100%;
transform: rotate(-90deg);
}
.nuda-sd2-ring__track {
fill: none;
stroke: rgba(255,255,255,.08);
stroke-width: 4;
}
.nuda-sd2-ring__bar {
fill: none;
stroke: #e4ff54;
stroke-width: 4;
stroke-linecap: round;
stroke-dasharray: 113;
stroke-dashoffset: 113;
animation: nuda-sd2-ring-fill linear;
animation-timeline: scroll(nearest);
}
.nuda-sd2-ring__content {
color: #a1a1aa;
font-size: .75rem;
line-height: 1.6;
}
.nuda-sd2-ring__content p {
margin: 0 0 1.2rem;
}
@keyframes nuda-sd2-ring-fill {
to {
stroke-dashoffset: 0;
}
}
@supports not (animation-timeline:scroll()) {
.nuda-sd2-ring__bar {
stroke-dashoffset: 40;
}
}
@media (prefers-reduced-motion:reduce) {
.nuda-sd2-ring__bar {
animation: none;
animation-timeline: none;
stroke-dashoffset: 0;
}
}
How to use Sticky Progress Ring
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.