What the CSS loader generator does
A loading indicator built in CSS needs no image, no SVG file and no JavaScript, scales to any size, and recolours with one property. This generator offers five classic patterns — spinner, dots, bars, pulse and bounce — with controls for size, colour and speed, a live preview, and the HTML and CSS to paste. Runs in your browser.
The five patterns
| Loader | How it is built | Best for |
|---|---|---|
| Spinner | A circle with a border, one side coloured, rotating | Buttons, inline waits, the general-purpose choice |
| Dots | Three circles scaling or fading in sequence | Chat 'typing' indicators, small inline spaces |
| Bars | Vertical bars stretching in a staggered wave | Audio, processing, dashboards |
| Pulse | A circle expanding and fading | Live status, recording, connection indicators |
| Bounce | Two or three balls bouncing with staggered delays | Playful interfaces, empty states |
Anatomy of the spinner
.spinner {
width: 40px; height: 40px;
border: 4px solid rgba(0, 0, 0, 0.1);
border-top-color: #E85D26;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }The trick is a circular border where only one side is coloured; rotating the element spins the coloured arc. transform animates on the compositor, so it stays smooth even when the main thread is busy — which is precisely when a loader is shown. Staggered patterns (dots, bars, bounce) use the same keyframes on several elements with increasing animation-delay.
Sizing, colour and speed
- Size: 16–20 px inside buttons and inputs; 32–48 px for content areas; 64 px+ for full-page overlays. The generator scales all parts together.
- Colour: use
currentColorso the loader inherits the text colour of its container, or the brand accent. Keep the track (the faint ring) at 10–20% opacity. - Speed: 0.6–1 s per revolution for spinners; 1–1.4 s per cycle for dots and bars. Faster looks frantic, slower looks stuck.
- Easing: linear for rotation; ease-in-out for scale and bounce.
Using loaders well
- Show a loader only for waits over about 300 ms; a flash of spinner for a fast response looks worse than nothing. Delay its appearance with a short
animation-delayon opacity. - For waits over a few seconds, prefer a progress bar or a skeleton screen that shows the layout to come; spinners give no sense of progress.
- Put the loader where the content will appear, not in a corner.
- Disable the triggering button and show the spinner inside it to prevent double submits.
Accessibility
<div class="spinner" role="status" aria-live="polite">
<span class="visually-hidden">Loading…</span>
</div>- Add
role="status"and visually hidden text so screen readers announce the wait. - Under
@media (prefers-reduced-motion: reduce), slow the animation or replace it with a static indicator and text. - Ensure the loader colour has contrast against its background; a light grey ring on white is easy to miss.
Frequently asked questions
Why does my spinner stutter?
Something else is animating a layout property (width, top) on the main thread, or the page is genuinely blocked by JavaScript. Keep loaders on transform and opacity only.
How do I centre it on the page?
Wrap it in a container with display: grid; place-items: center; and full height, or position: fixed; inset: 0 for an overlay.
Can I use a gradient arc instead of a solid one?
Yes — use conic-gradient as the background with a mask to cut the ring, instead of the border trick. It looks smoother but needs mask support.
How do I stop the spinner when loading finishes?
Remove the element or set animation-play-state: paused and hide it. Do not leave invisible spinners animating; they still cost CPU.
Is a GIF loader ever better?
Only for email or environments without CSS animation. CSS loaders are smaller, sharper and recolourable.
Do these work in all browsers?
Yes. CSS animations and transforms have been supported everywhere for over a decade.