SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/spinners

7 CSS Spinners

7 Pure CSSPublished 2026-10-05Free · no sign-up

Seven spinners, each a single element or a few small ones: two counter-rotating arcs, a segmented system-style spinner, a gradient arc like a comet, a growing and shrinking SVG arc, dots that pulse round a circle, a flipping square and one inline spinner in three sizes that takes its colour from the text.

01. Dual Ring Spinner

SpinnerRing

Two arcs spin in opposite directions, one inside the other, made from borders with two transparent sides. The wrapper has role="status" with a visually hidden Loading label, which is what every spinner needs.

View code
<div class="sp1" role="status"><span class="sr">Loading</span></div>
.sp1 { position: relative; width: 56px; height: 56px; }
.sp1::before, .sp1::after { content: ""; position: absolute; border-radius: 50%; border: 4px solid transparent; }
.sp1::before { inset: 0; border-top-color: #38bdf8; border-right-color: #38bdf8; animation: sp1 1s linear infinite; }
.sp1::after { inset: 10px; border-bottom-color: #f472b6; border-left-color: #f472b6; animation: sp1 .8s linear infinite reverse; }
.sp1 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp1 { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .sp1::before, .sp1::after { animation-duration: 3s; } }

02. Segmented Fading Spinner

SpinnerSegments

Twelve short bars arranged in a circle fade in sequence, the classic system activity indicator. Each bar is rotated into place with a custom property and shares one fade animation offset by a twelfth of the cycle.

View code
<div class="sp2" role="status"><span class="sr">Loading</span><i style="--i:0"></i><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i><i style="--i:4"></i><i style="--i:5"></i><i style="--i:6"></i><i style="--i:7"></i><i style="--i:8"></i><i style="--i:9"></i><i style="--i:10"></i><i style="--i:11"></i></div>
.sp2 { position: relative; width: 48px; height: 48px; }
.sp2 i { position: absolute; left: 22px; top: 2px; width: 4px; height: 12px; border-radius: 2px; background: #344054; transform-origin: 2px 22px; transform: rotate(calc(var(--i) * 30deg)); animation: sp2 1.2s linear infinite; animation-delay: calc(var(--i) * .1s - 1.2s); }
.sp2 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp2 { from { opacity: 1; } to { opacity: .15; } }
@media (prefers-reduced-motion: reduce) { .sp2 i { animation-duration: 3.6s; } }

03. Gradient Arc Spinner

SpinnerConic

A ring that fades from transparent to solid around its length, so it looks like a comet chasing its own tail. The colour is a conic gradient and the hole in the middle is a radial mask, so it is a single element.

View code
<div class="sp3" role="status"><span class="sr">Loading</span></div>
.sp3 { width: 56px; height: 56px; border-radius: 50%; background: conic-gradient(transparent, #a78bfa); -webkit-mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px)); mask: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 5px)); animation: sp3 .9s linear infinite; }
.sp3 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp3 { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) { .sp3 { animation-duration: 3s; } }

04. Growing and Shrinking SVG Arc

SpinnerSVG

The arc lengthens and shortens while the whole circle rotates, the material-style spinner. Two animations combine: stroke-dasharray changes the visible arc and a rotate on the svg spins it, at different durations so the pattern never quite repeats.

View code
<svg class="sp4" viewBox="0 0 50 50" role="status" aria-label="Loading"><circle cx="25" cy="25" r="20"/></svg>
.sp4 { width: 52px; height: 52px; animation: sp4r 2s linear infinite; }
.sp4 circle { fill: none; stroke: #2563eb; stroke-width: 4.5; stroke-linecap: round; stroke-dasharray: 1 150; stroke-dashoffset: 0; animation: sp4d 1.5s ease-in-out infinite; }
@keyframes sp4r { to { transform: rotate(360deg); } }
@keyframes sp4d { 0% { stroke-dasharray: 1 150; stroke-dashoffset: 0; } 50% { stroke-dasharray: 90 150; stroke-dashoffset: -35; } 100% { stroke-dasharray: 90 150; stroke-dashoffset: -124; } }
@media (prefers-reduced-motion: reduce) { .sp4 { animation-duration: 6s; } .sp4 circle { animation: none; stroke-dasharray: 60 150; } }

05. Dots Around a Circle

SpinnerDots

Eight dots on a circle grow and fade one after another, giving a rotating feel without anything actually rotating. Each dot is positioned with rotate then translate and scaled by a shared keyframe with a staggered delay.

View code
<div class="sp5" role="status"><span class="sr">Loading</span><i style="--i:0"></i><i style="--i:1"></i><i style="--i:2"></i><i style="--i:3"></i><i style="--i:4"></i><i style="--i:5"></i><i style="--i:6"></i><i style="--i:7"></i></div>
.sp5 { position: relative; width: 56px; height: 56px; }
.sp5 i { position: absolute; left: 24px; top: 24px; width: 8px; height: 8px; border-radius: 50%; background: #34d399; transform: rotate(calc(var(--i) * 45deg)) translateY(-22px) scale(.4); animation: sp5 1s linear infinite; animation-delay: calc(var(--i) * .125s - 1s); }
.sp5 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp5 { 0% { transform: rotate(calc(var(--i) * 45deg)) translateY(-22px) scale(1); } 100% { transform: rotate(calc(var(--i) * 45deg)) translateY(-22px) scale(.4); } }
@media (prefers-reduced-motion: reduce) { .sp5 i { animation-duration: 3s; } }

06. Flipping Square

Spinner3D

A square that flips over its x axis, then its y axis, in a perspective space, an old favourite that still reads clearly as busy. The rotations are four keyframes, each a half turn on alternating axes.

View code
<div class="sp6" role="status"><span class="sr">Loading</span></div>
.sp6 { width: 40px; height: 40px; border-radius: 6px; background: #db2777; animation: sp6 1.4s ease-in-out infinite; }
.sp6 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp6 { 0% { transform: perspective(120px) rotateX(0) rotateY(0); } 50% { transform: perspective(120px) rotateX(-180deg) rotateY(0); } 100% { transform: perspective(120px) rotateX(-180deg) rotateY(-180deg); } }
@media (prefers-reduced-motion: reduce) { .sp6 { animation-duration: 4s; } }

07. Spinner Sizes with currentColor

SpinnerInline

One spinner class in three sizes, inside a button, beside text and on its own, all inheriting their colour from the surrounding text through currentColor. Size comes from the font size in em, so the spinner always matches its context.

View code
<div class="sp7"><button disabled><span class="spin" aria-hidden="true"></span>Saving</button><p><span class="spin" aria-hidden="true"></span>Checking availability…</p><div class="big" role="status"><span class="spin"></span><span class="sr">Loading results</span></div></div>
.sp7 { display: grid; justify-items: center; gap: 14px; font-family: Inter, system-ui, sans-serif; }
.sp7 .spin { display: inline-block; width: 1em; height: 1em; border: .14em solid currentColor; border-right-color: transparent; border-radius: 50%; vertical-align: -.15em; animation: sp7 .7s linear infinite; }
.sp7 button { display: inline-flex; align-items: center; gap: 8px; padding: 10px 16px; border: 0; border-radius: 8px; background: #4f46e5; color: #fff; font: 700 14px Inter, system-ui, sans-serif; }
.sp7 p { margin: 0; display: flex; align-items: center; gap: 8px; color: #fbbf24; font-size: 14px; }
.sp7 .big { color: #38bdf8; font-size: 40px; }
.sp7 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes sp7 { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .sp7 .spin { animation-duration: 2.4s; } }

About these CSS Spinners

Spinners need words

A spinner means nothing to a screen reader. Wrap it in role="status" with a visually hidden label such as Loading results, so the wait is announced.

Use currentColor and em

Draw the spinner with currentColor and size it in em, and it automatically matches any button or line of text you put it in.

Spin with transform

Rotate with transform: rotate() and a linear timing function. Linear is the one place it is right: a spinner that eases in and out looks like it is stuttering.

When not to spin

Spinners suit waits of one to ten seconds with no known progress. For longer or measurable work, show a progress bar; for very short waits, show nothing at all to avoid a flash.

Frequently asked questions

Should spinners stop for reduced motion?

Slow them down instead. A frozen spinner can look like the page has stalled.

How is this page different from the loaders page?

Loaders covers dots, bars and skeletons; this page is rotating spinners only.

Can I use these on a dark and light background?

Yes. The inline spinner follows the text colour; the others take a single colour to change.