CSS Loader Generator
Generate CSS loading spinners and animations — 8 presets, live preview, copy HTML + CSS
Live Preview
HTML
<div class="loader" aria-label="Loading" role="status"></div>
CSS
.loader {
width: 48px;
height: 48px;
border: 4px solid rgba(59,130,246,0.2);
border-top: 4px solid #3B82F6;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}Every user-facing application needs loading states. The alternatives — GIF images, JavaScript libraries, or SVG icons requiring external files — all introduce dependencies, extra HTTP requests, or bloated bundles. Pure CSS loaders are the opposite: zero dependencies, one <div>, a few keyframe rules, and you get a smooth 60 fps animation that respects the user’s motion preferences and works in every modern browser.
Why Pure CSS Loaders Beat GIFs and Icon Libraries
GIF loaders were the original approach. They still appear in legacy codebases. The problems: fixed color, fixed size, jagged edges on high-DPI screens, and you need a separate file for every color variant. A GIF that looks fine on a white background becomes invisible on a dark theme.
SVG icon libraries (Heroicons, Feather, etc.) typically include a static spinner icon, not an animated one. You still need CSS animation or JavaScript to make it spin — so you have both an SVG dependency and CSS animation code.
JavaScript animation libraries (Lottie, Three.js particle loaders) are powerful but massive overkill for a simple loading state. They block the main thread during parse and execution.
Pure CSS loaders have none of these drawbacks: they scale to any size with width/height, support any color with a single property change, render crisply on retina displays, run on the GPU compositor thread (for transform-based animations), and weigh under 30 lines of code.
The 8 Loader Types
Spinner — The universal loading indicator. A circle with one colored arc rotating continuously. Works in any context because users globally recognize it as “processing.”
Dual Ring — Two opposing arcs create a yin-yang effect. More visually balanced than a single-arc spinner; useful in centered loading overlays.
Ring — Two concentric circles spinning at different speeds. Creates a subtle depth illusion that works well on dashboards and data-heavy interfaces.
Dots — Three bouncing circles with staggered animation delays. Commonly used for “typing indicator” or chat-style loading states. Feels more conversational than mechanical.
Pulse — A single circle that scales in and out. The simplest possible loader — one element, one animation. Ideal for inline status indicators or icon-adjacent states.
Bars — Four vertical bars that scale up and down in sequence, resembling a music equalizer. Natural fit for audio, data streaming, or file upload contexts.
Ellipsis — Four dots that scale in sequence, creating a “wave” effect. Slightly more elaborate than the bouncing dots; conveys sequential processing.
Ripple — Two concentric rings that expand outward from a center point, fading as they grow. Evokes a radar or sonar ping. Works beautifully for search and “finding” states.
Accessibility: Making Loaders Usable for Everyone
Raw CSS animations are invisible to screen readers. Two additions fix this:
<div class="loader" role="status" aria-label="Loading"></div>
The role="status" tells assistive technology this is a live region. The aria-label provides the spoken text. This tool includes both attributes in the generated HTML.
Respecting prefers-reduced-motion
Some users configure their OS to reduce animations due to vestibular disorders or motion sensitivity. Respect this preference:
@media (prefers-reduced-motion: reduce) {
.loader {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
}
}
Add this rule after the generated CSS to pause animations for affected users. Alternatively, replace the animation entirely with a static indicator in the reduced-motion branch.
Performance Characteristics
The loaders in this generator animate only transform and opacity — the two CSS properties that run entirely on the GPU compositor thread. This means they never trigger layout recalculation or repaint, even while the main JavaScript thread is busy. The result is smooth animation even during heavy data processing.
Properties to avoid in loading animations for performance: width, height, top, left, margin, padding, background-color (mid-animation). These trigger layout or paint operations on every frame.
Customization Guide
Size: All loaders scale proportionally. The child elements (dots, bars, ring thickness) are derived from the outer size as ratios, so changing the size slider always produces a well-proportioned result.
Color: The color input sets the primary accent. For ring-based loaders, the track color is automatically calculated as a 20% opacity version of the same hue, keeping the contrast ratio consistent.
Speed: Slower speeds (1.5–3s) feel more relaxed and are less distracting in the background. Faster speeds (0.3–0.7s) communicate urgency — use for actions the user is actively waiting on. The default 1s is a neutral middle ground validated across many design systems.
Border width: For ring-type loaders (Spinner, Dual Ring, Ring, Ripple), thicker borders create a bolder, more confident look. Thinner borders feel lighter and more refined.
Integration Patterns
Inline replacement: Replace a button’s label with a loader while a form submits:
<button id="submit">
<span class="btn-text">Save</span>
<div class="loader" hidden></div>
</button>
submitBtn.addEventListener('click', () => {
submitBtn.querySelector('.btn-text').hidden = true;
submitBtn.querySelector('.loader').hidden = false;
});
Full-screen overlay: Center a loader over the entire viewport during page-level operations:
.loading-overlay {
position: fixed;
inset: 0;
background: rgba(255, 255, 255, 0.85);
display: grid;
place-items: center;
z-index: 9999;
}
Skeleton loaders: For content that has a known layout, prefer skeleton screens (gray placeholder boxes) over spinners — they reduce perceived load time because users can see the page structure.
Frequently Asked Questions
Do these loaders work without JavaScript?
Yes. Every loader is pure HTML and CSS. No JavaScript is required for the animation. JavaScript is only needed if you want to show or hide the loader dynamically (adding/removing a class or hidden attribute).
Can I use multiple loader types on the same page?
Yes. Each loader uses a scoped class name (.loader, .dots, .bars, etc.). If you use multiple types, rename the classes to avoid conflicts — for example, .spinner-loader and .dots-loader.
Why does the animation pause when the browser tab is hidden?
Browsers suspend requestAnimationFrame and reduce CSS animation tick rates for background tabs to save CPU. This is expected behavior and not a bug. The animation resumes instantly when the tab regains focus.
How do I change the loader color dynamically with JavaScript?
Use a CSS custom property. Replace the hard-coded color in the generated CSS with var(--loader-color, #3B82F6), then set element.style.setProperty('--loader-color', '#EF4444') from JavaScript.
Does this tool send my configuration to a server? No. All preview rendering, code generation, and output happen entirely in your browser. No data is sent anywhere.