25 CSS Spinners23 / 25
Progress Arc Fill Spinner
An SVG circle arc fills from empty to complete on a two-second loop, with a 100% percentage counter label centred inside — suitable as both a loading indicator and a determinate progress ring.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="sp-23">
<svg class="sp-23__svg" viewBox="0 0 90 90">
<circle class="sp-23__track" cx="45" cy="45" r="40"/>
<circle class="sp-23__arc" cx="45" cy="45" r="40"/>
<text class="sp-23__percent" x="45" y="45">100%</text>
</svg>
</div><div class="sp-23">
<svg class="sp-23__svg" viewBox="0 0 90 90">
<circle class="sp-23__track" cx="45" cy="45" r="40"/>
<circle class="sp-23__arc" cx="45" cy="45" r="40"/>
<text class="sp-23__percent" x="45" y="45">100%</text>
</svg>
</div>.sp-23,
.sp-23 *,
.sp-23 *::before,
.sp-23 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-23 {
--bg: #050a0f;
--c: #00e5ff;
--track: rgba(0,229,255,0.12);
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg);
}
.sp-23__svg {
width: 90px;
height: 90px;
transform: rotate(-90deg);
filter: drop-shadow(0 0 6px var(--c));
}
.sp-23__track {
fill: none;
stroke: var(--track);
stroke-width: 5;
}
.sp-23__arc {
fill: none;
stroke: var(--c);
stroke-width: 5;
stroke-linecap: round;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: sp-23-fill 2s ease-in-out infinite;
}
.sp-23__percent {
fill: var(--c);
font-size: 14px;
font-family: system-ui,sans-serif;
font-weight: 600;
text-anchor: middle;
dominant-baseline: central;
transform: rotate(90deg);
transform-origin: 45px 45px;
animation: sp-23-count 2s ease-in-out infinite;
letter-spacing: -0.5px;
}
@keyframes sp-23-fill {
0% {
stroke-dashoffset: 251;
opacity: 1;
}
80% {
stroke-dashoffset: 0;
opacity: 1;
}
100% {
stroke-dashoffset: 0;
opacity: 0;
}
}
@keyframes sp-23-count {
0% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-23__arc {
animation: none;
stroke-dashoffset: 100;
}
.sp-23__percent {
animation: none;
}
}.sp-23,
.sp-23 *,
.sp-23 *::before,
.sp-23 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-23 {
--bg: #050a0f;
--c: #00e5ff;
--track: rgba(0,229,255,0.12);
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg);
}
.sp-23__svg {
width: 90px;
height: 90px;
transform: rotate(-90deg);
filter: drop-shadow(0 0 6px var(--c));
}
.sp-23__track {
fill: none;
stroke: var(--track);
stroke-width: 5;
}
.sp-23__arc {
fill: none;
stroke: var(--c);
stroke-width: 5;
stroke-linecap: round;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: sp-23-fill 2s ease-in-out infinite;
}
.sp-23__percent {
fill: var(--c);
font-size: 14px;
font-family: system-ui,sans-serif;
font-weight: 600;
text-anchor: middle;
dominant-baseline: central;
transform: rotate(90deg);
transform-origin: 45px 45px;
animation: sp-23-count 2s ease-in-out infinite;
letter-spacing: -0.5px;
}
@keyframes sp-23-fill {
0% {
stroke-dashoffset: 251;
opacity: 1;
}
80% {
stroke-dashoffset: 0;
opacity: 1;
}
100% {
stroke-dashoffset: 0;
opacity: 0;
}
}
@keyframes sp-23-count {
0% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-23__arc {
animation: none;
stroke-dashoffset: 100;
}
.sp-23__percent {
animation: none;
}
}Here's a working CSS Spinner from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Progress Arc Fill Spinner
Source: https://codefronts.com/motion/css-spinner-designs/progress-arc-fill-spinner/
An SVG circle arc fills from empty to complete on a two-second loop, with a 100% percentage counter label centred inside — suitable as both a loading indicator and a determinate progress ring.
## HTML
```html
<div class="sp-23">
<svg class="sp-23__svg" viewBox="0 0 90 90">
<circle class="sp-23__track" cx="45" cy="45" r="40"/>
<circle class="sp-23__arc" cx="45" cy="45" r="40"/>
<text class="sp-23__percent" x="45" y="45">100%</text>
</svg>
</div>
```
## CSS
```css
.sp-23,
.sp-23 *,
.sp-23 *::before,
.sp-23 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-23 {
--bg: #050a0f;
--c: #00e5ff;
--track: rgba(0,229,255,0.12);
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg);
}
.sp-23__svg {
width: 90px;
height: 90px;
transform: rotate(-90deg);
filter: drop-shadow(0 0 6px var(--c));
}
.sp-23__track {
fill: none;
stroke: var(--track);
stroke-width: 5;
}
.sp-23__arc {
fill: none;
stroke: var(--c);
stroke-width: 5;
stroke-linecap: round;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: sp-23-fill 2s ease-in-out infinite;
}
.sp-23__percent {
fill: var(--c);
font-size: 14px;
font-family: system-ui,sans-serif;
font-weight: 600;
text-anchor: middle;
dominant-baseline: central;
transform: rotate(90deg);
transform-origin: 45px 45px;
animation: sp-23-count 2s ease-in-out infinite;
letter-spacing: -0.5px;
}
@keyframes sp-23-fill {
0% {
stroke-dashoffset: 251;
opacity: 1;
}
80% {
stroke-dashoffset: 0;
opacity: 1;
}
100% {
stroke-dashoffset: 0;
opacity: 0;
}
}
@keyframes sp-23-count {
0% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-23__arc {
animation: none;
stroke-dashoffset: 100;
}
.sp-23__percent {
animation: none;
}
}
```Here's a working CSS Spinner from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Progress Arc Fill Spinner
Source: https://codefronts.com/motion/css-spinner-designs/progress-arc-fill-spinner/
An SVG circle arc fills from empty to complete on a two-second loop, with a 100% percentage counter label centred inside — suitable as both a loading indicator and a determinate progress ring.
## HTML
```html
<div class="sp-23">
<svg class="sp-23__svg" viewBox="0 0 90 90">
<circle class="sp-23__track" cx="45" cy="45" r="40"/>
<circle class="sp-23__arc" cx="45" cy="45" r="40"/>
<text class="sp-23__percent" x="45" y="45">100%</text>
</svg>
</div>
```
## CSS
```css
.sp-23,
.sp-23 *,
.sp-23 *::before,
.sp-23 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.sp-23 {
--bg: #050a0f;
--c: #00e5ff;
--track: rgba(0,229,255,0.12);
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: var(--bg);
}
.sp-23__svg {
width: 90px;
height: 90px;
transform: rotate(-90deg);
filter: drop-shadow(0 0 6px var(--c));
}
.sp-23__track {
fill: none;
stroke: var(--track);
stroke-width: 5;
}
.sp-23__arc {
fill: none;
stroke: var(--c);
stroke-width: 5;
stroke-linecap: round;
stroke-dasharray: 251;
stroke-dashoffset: 251;
animation: sp-23-fill 2s ease-in-out infinite;
}
.sp-23__percent {
fill: var(--c);
font-size: 14px;
font-family: system-ui,sans-serif;
font-weight: 600;
text-anchor: middle;
dominant-baseline: central;
transform: rotate(90deg);
transform-origin: 45px 45px;
animation: sp-23-count 2s ease-in-out infinite;
letter-spacing: -0.5px;
}
@keyframes sp-23-fill {
0% {
stroke-dashoffset: 251;
opacity: 1;
}
80% {
stroke-dashoffset: 0;
opacity: 1;
}
100% {
stroke-dashoffset: 0;
opacity: 0;
}
}
@keyframes sp-23-count {
0% {
opacity: 1;
}
80% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
.sp-23__arc {
animation: none;
stroke-dashoffset: 100;
}
.sp-23__percent {
animation: none;
}
}
```How this works
An SVG circle element uses stroke-dasharray:251 (the circumference of a 40px-radius circle: 2π×40 ≈ 251px) and starts at stroke-dashoffset:251 (fully hidden). The sp-23-fill keyframe animates the offset from 251 to 0 (fully filled arc) over 80% of the cycle, then briefly holds and fades — simulating a completion event before the next cycle begins.
The SVG is rotated -90deg in CSS so the arc starts at the 12 o'clock position rather than 3 o'clock (SVG's default start angle). The percentage text uses a counter-rotation transform so it reads upright despite the SVG's rotation. A faint track circle at the same radius provides the empty-arc guide.
Make it yours
- Change stroke colour via
--c— adapt to brand by matching primary action colour. - Set
stroke-dashoffsetto a specific value (e.g.125for 50%) using inline style or JS to display static progress instead of animating. - Add a second counter arc on the track circle using
stroke-dashoffsetanimation in reverse to show remaining capacity. - Change
stroke-linecap:roundtosquarefor sharp arc ends more suitable for geometric/technical UIs. - Increase the SVG
viewBoxand circle radius proportionally for a larger progress ring — recalculate thestroke-dasharrayas2 * Math.PI * radius.
Gotchas — read before shipping
- The circumference value (
251) is specific tor="40"— if you change the radius, recalculate as2 * Math.PI * rand updatestroke-dasharrayaccordingly. - The
transform:rotate(-90deg)on the SVG element causes the percent text counter-rotation viatransform-origin:45px 45px(the SVG's centre) — changing the SVG dimensions requires updating this origin. stroke-dashoffsetanimation on an SVG circle is not composited (it triggers SVG repaint rather than GPU compositing) — acceptable for a single spinner but avoid dozens on one page.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 60+ | 12+ | 60+ | 60+ |
SVG stroke animation universally supported; no modern CSS features required.