CSS Loader Generator
Pick a pure-CSS loading spinner from the gallery, tune its colour, size and speed on a live preview, and copy the ready-to-use HTML and CSS. No JavaScript — every loader is animated by CSS alone.
Element HTML
CSS
The output is pure CSS — no JavaScript. The preview and both code snippets are built on your own device; there are no network calls and the page keeps working offline.
A curated set of pure-CSS loaders
This is a finished gallery of nine loading spinners, each animated entirely with CSS — a @keyframes block and a class rule, no JavaScript at all. You pick one, adjust its colour, size and speed, and paste the two snippets into your project. Because the loader is pure CSS, it appears the moment the stylesheet parses — before any script has even downloaded — which is precisely when a page most needs to signal that something is happening. It is a deliberately bounded set: nine distinct animations, honestly counted, rather than a padded list of near-identical variants.
The nine styles, and how they move
| Style | Animation | Uses thickness? |
|---|---|---|
| Ring | a single C-shaped arc rotating 360° | yes |
| Dual ring | two opposing arcs sweeping in a circle | yes |
| Pulse dots | three dots fading and scaling in sequence | no |
| Bars | equalizer bars rising and falling | no |
| Ripple | two rings expanding outward and fading | yes |
| Pulse | one disc breathing — scaling up and down as it fades | no |
| Flip | a square flipping on both axes in 3D | no |
| Orbit | a dot circling an invisible track | no |
| Bounce | a single disc bouncing up and down | no |
The ring, dual-ring and ripple styles are drawn with a CSS border, so the thickness control changes their stroke width; the others are solid shapes or dots where thickness does not apply, so that control hides itself when it would do nothing.
What the code looks like
Most styles are a single element. The default ring, for instance, is just this:
<div class="loader"></div>
.loader {
width: 48px;
height: 48px;
border: 5px solid #4f52f0;
border-bottom-color: transparent;
border-radius: 50%;
box-sizing: border-box;
display: inline-block;
animation: loader-ring 1s linear infinite;
}
@keyframes loader-ring {
to { transform: rotate(360deg); }
}
The trick is border-bottom-color: transparent: three sides of the circle are painted and one is invisible, so the rotating ring reads as a moving gap. The dots and bars styles need three children, so their HTML wraps three <span> elements — the HTML box always shows the exact structure to paste, whatever the style.
Using a loader well
A loader runs forever by design — its animation-iteration-count is infinite — so you show it by adding the element to the page and stop it by removing that element once your content is ready. Keep it decorative: mark the element aria-hidden="true" and put a real text status like "Loading…" nearby so screen-reader users are told what is happening, since a spinning shape means nothing to them. And respect motion preferences — wrapping the animation in @media (prefers-reduced-motion: reduce) to pause or simplify it is a small courtesy to anyone who finds spinners uncomfortable.
Honest limits
These loaders are indeterminate: they show that work is happening, not how far along it is. For a known duration or a measurable transfer, a real progress bar communicates far more, and none of these substitute for one. Performance-wise, eight of the nine animate only transform and opacity, which the browser composites on the GPU without re-running layout — cheap even on weak hardware. The exception is ripple, which animates width and height; that is fine for a single spinner but avoid stacking dozens of them. Finally, these are visual defaults you will likely want to match to your brand — that is what the colour, size and speed controls are for. If you need a completely bespoke animation rather than one of these presets, build it from scratch in the CSS Animation Generator, shape a custom easing curve with the cubic-bezier() Generator, or add depth with the Box-Shadow Generator.