CSS Loader Generator
Create animated CSS loading spinners with customisable style, size, colour, and speed. Copy the generated CSS and HTML with one click.
This CSS loader generator creates five types of animated loading indicators - spinner, dots, bars, pulse, and bounce - using pure CSS keyframes. Pick a style, adjust the size, colour, speed, and border width, then copy the generated CSS and HTML. All animations run entirely in the browser with no JavaScript dependency, keeping file sizes small and performance high.
About CSS Loader Generator
How CSS Loading Animations Work
CSS animations are built on two core features: @keyframes rules and the animation shorthand property. A @keyframes rule defines what the element looks like at various points during the animation cycle, and the animation property applies that sequence to an element with a specified duration, timing function, and iteration count.
For a basic spinner, the keyframes define a rotation from 0 to 360 degrees. The animation property ties it all together: animation: spin 0.8s linear infinite. The linear timing function keeps the rotation speed constant, and infinite means it loops until the element is removed from the DOM.
Staggered animations - used by the dots, bars, and bounce styles in this generator - rely on animation-delay. Each child element starts the same keyframe sequence at a slightly different time, creating the visual effect of elements moving in sequence. Three dots might use delays of 0s, 0.15s, and 0.3s to produce a wave-like pulse effect.
| CSS Property | Purpose | Example |
|---|---|---|
| @keyframes | Defines the animation sequence at each stage | @keyframes spin { to { transform: rotate(360deg) } } |
| animation-name | Links an element to a specific keyframes rule | spin |
| animation-duration | How long one full cycle takes | 0.8s |
| animation-timing-function | Controls the speed curve through each cycle | linear, ease-in-out |
| animation-iteration-count | How many times the animation repeats | infinite |
| animation-delay | Offsets the start time for staggered effects | 0.15s on the second dot, 0.3s on the third |
Modern browsers handle these animations on the GPU compositor thread when the animated properties are limited to transform and opacity. According to MDN Web Docs, CSS transitions and keyframe animations on these two properties automatically run on the compositor without triggering layout or paint operations, leaving the main thread free for JavaScript execution.
Worked example - building a spinner from scratch: Start with a square div sized at 48px. Apply a 4px solid border in light grey (#e5e7eb), then override the top border with your accent colour (border-top: 4px solid #10b981). Add border-radius: 50% to make it circular. The transparent top border creates the visual gap in the ring. Now create the keyframes: @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }. Apply the animation with animation: spin 0.8s linear infinite. The result is a smoothly rotating ring that weighs under 200 bytes of CSS - far smaller than any image-based alternative.
Which Loader Style Should You Use?
| Style | Animation | Best For | CSS Technique |
|---|---|---|---|
| Spinner | Rotating ring with a transparent gap | General loading states, buttons, forms | border + border-top-color: transparent + rotate keyframe |
| Dots | Three circles pulsing in sequence | Chat typing indicators, inline loading text | Multiple child elements + animation-delay offsets |
| Bars | Vertical bars scaling up and down | Audio visualisations, progress displays | scaleY transform with staggered delays |
| Pulse | Circle that breathes in and out | Subtle background loading, placeholder states | scale transform combined with opacity change |
| Bounce | Balls jumping in alternating sequence | Playful interfaces, casual or gaming apps | translateY with staggered animation-delay |
The spinner is the most universally recognised loading indicator - users immediately understand what it means without any learned context. Dots work well at small sizes and blend naturally into text-heavy interfaces, which is why most chat applications use them for typing indicators. Bars suit dashboards or media-heavy contexts where the vertical motion feels thematic. The pulse is the most subtle option, good for situations where loading is secondary to the content already on screen. Bounce loaders add personality and work best in apps with a casual or playful visual tone.
Each style in this generator can be tuned through four controls: size (16px to 120px), colour (any hex value), speed (0.2s to 3s animation duration), and border width (2px to 12px, spinner only). The live preview shows the result on both light and dark backgrounds so you can check contrast before copying the code.
Performance: Why CSS Loaders Beat the Alternatives
Pure CSS loading animations have a significant performance advantage over GIFs, animated PNGs, and JavaScript-based alternatives. The key reason is GPU compositing. When you animate only transform and opacity, the browser creates a separate compositing layer for the element and offloads rendering to the GPU. The main thread - where JavaScript executes - is bypassed entirely.
| Approach | Typical Size | GPU Accelerated | Customisable | Drawbacks |
|---|---|---|---|---|
| CSS keyframes | Under 1 KB | Yes (transform/opacity) | Fully via CSS variables | Complex multi-step sequences can get verbose |
| Animated GIF | 5-50 KB | No | No (baked into file) | Fixed colours, no transparency control, heavy |
| JavaScript library | 10-80 KB (library) | Depends on implementation | Yes | Adds dependency weight, runs on main thread |
| SVG animation (SMIL) | 2-10 KB | Partial | Yes via attributes | No IE support, limited tooling, harder to debug than CSS |
| Lottie (JSON) | 5-100 KB + player | Depends on renderer | Yes via props | Requires Lottie player library (50 KB+) |
According to Smashing Magazine's analysis of GPU animation, composited animations stay smooth at 60fps even during heavy JavaScript computation because the compositor thread operates independently. This matters most during the exact moments you display a loader - the page is typically busy fetching data or processing a response, so keeping the animation off the main thread prevents stuttering.
A note on the will-change CSS property: it hints to the browser that an element will animate, but overusing it wastes GPU memory. For always-visible loaders like these, the browser already optimises automatically based on the animation declaration, so adding will-change is unnecessary. Reserve it for elements that animate only sometimes, like hover effects or route transitions.
When Should You Show a Loading Indicator?
Not every wait needs a spinner. Research from the Nielsen Norman Group found that loading indicators reduce perceived wait time, with research suggesting waits accompanied by visual feedback feel noticeably shorter than identical waits without feedback. But showing a spinner too early or for too brief a moment can actually make an interface feel slower.
| Wait Duration | Recommended Indicator | Reason |
|---|---|---|
| Under 200ms | None | The operation feels instant - any indicator would flash and distract |
| 200ms - 1 second | Subtle change (button disable, opacity shift) | Acknowledges the action without drawing heavy attention |
| 1 - 10 seconds | Spinner or dots loader | The sweet spot for looped animations - long enough to register, short enough to tolerate |
| 10+ seconds | Progress bar with percentage | Users lose patience with indefinite spinners past 10 seconds and may assume the page is frozen |
A common implementation pattern is to delay showing the spinner by 300-400ms using JavaScript. If the data arrives before the delay fires, no spinner appears at all, avoiding the flash-of-loading-state that makes fast operations feel sluggish. Once shown, keep the spinner visible for at least 500ms even if the data arrives immediately after, to prevent a jarring appear-and-vanish flicker.
Making CSS Loaders Accessible
Loading animations need accessibility consideration beyond just visual design. The W3C WCAG 2.2 technique C39 specifically addresses the prefers-reduced-motion media query as a method for meeting success criterion 2.3.3 (Animation from Interactions). Users with vestibular disorders can experience dizziness, nausea, or headaches from motion on screen.
The recommended approach is to wrap animations inside a @media (prefers-reduced-motion: no-preference) block, so the loader stays static for users who have enabled the reduce-motion setting in their operating system. Every major browser supports this query, including Chrome, Firefox, Safari, and Edge. For screen reader users, add aria-live="polite" to the container that changes between loading and loaded states, so the transition is announced without interrupting current speech.
Here is an example of the reduced-motion pattern applied to a spinner:
@media (prefers-reduced-motion: reduce) { .loader { animation: none; } }
This stops the rotation entirely. An alternative approach is to slow the animation to a very gentle pace rather than stopping it, but the safest default is to remove motion completely.
Beyond reduced motion, consider colour-blind users. A spinner that relies on colour alone to indicate state (green for success, red for error) should also include a shape change or text label. For loading overlays, ensure the backdrop provides enough contrast that the spinner remains visible regardless of the page content beneath it - a semi-transparent dark overlay (rgba(0,0,0,0.4) or higher) works reliably on both light and dark content.
Common Implementation Patterns
A typical implementation wraps the loader in a container that toggles visibility based on a loading state variable. In React or Preact, this looks like: {isLoading && <div class="loader"></div>}. In vanilla JavaScript, toggle a CSS class on the container: el.classList.toggle('loading', isActive). The loader CSS itself stays the same regardless of framework.
For button loading states, a common pattern swaps the button text for a small inline spinner (16-20px) while disabling the button to prevent double submissions. Set pointer-events: none and reduce opacity to 0.7 on the button during loading, and restore both when the operation completes. This gives clear visual feedback without any layout shift.
Full-page loading overlays typically use position: fixed; inset: 0; with a flexbox container to centre the spinner. Add a z-index high enough to sit above all page content, and include a backdrop with background: rgba(0,0,0,0.4) to dim the page behind the loader.
Sizing and Placement Guidelines
| Context | Recommended Size | Placement Notes |
|---|---|---|
| Inline button | 16-20px | Replace the button text or sit beside it, matching the text line height |
| Card or section | 32-48px | Centred vertically and horizontally in the loading area |
| Full page overlay | 48-80px | Centred on a semi-transparent backdrop (rgba(0,0,0,0.4)) |
| App initial load | 64-120px | Splash screen style, often paired with the app logo |
For button loaders, replace the label text with the spinner while keeping the button width fixed (set a min-width on the button) to prevent layout shift. For page-level loaders, use position: fixed with inset: 0 and flexbox centering to keep the spinner in the viewport centre regardless of scroll position.
For custom easing curves in your loader animations, the CSS Cubic Bezier Generator lets you design timing functions visually - useful for making bounce or pulse animations feel more natural. For complementary visual effects like elevation and depth on loading overlays, the CSS Box Shadow Generator creates layered shadow styles. And the CSS Gradient Generator can create colourful skeleton screen placeholders that pair well with loading animations.
Sources
Frequently Asked Questions
What loader styles are available?
Five styles are included: spinner (rotating ring), dots (pulsing circles), bars (scaling vertical bars), pulse (breathing circle), and bounce (jumping balls). Each can be fully customised with size, colour, speed, and border width controls.
Can I customise the animation speed?
Yes. Use the speed slider to adjust the animation duration from 0.2 seconds (fast) to 3 seconds (slow). The live preview updates instantly so you can find the right feel.
Do the generated loaders use JavaScript?
No. All animations use pure CSS keyframes with no JavaScript required. The generated code includes only CSS and the minimal HTML structure needed.
Can I pause the animation to inspect it?
Yes. Click the Pause Animation button to freeze the loader. Click Resume to continue. The generated CSS is not affected by the pause state.
Is this free and private?
Yes. The generator runs entirely in your browser. No data is sent anywhere and the generated CSS is yours to use in any project.
Related Tools
Link to this tool
Copy this HTML to link to this tool from your website or blog.
<a href="https://toolboxkit.io/tools/css-loader-generator/" title="CSS Loader Generator - Free Online Tool">Try CSS Loader Generator on ToolboxKit.io</a>