Build a pure-CSS loading spinner or progress animation — no JavaScript, no images.
Generated live in your browser.
How do you make a loading spinner with CSS?
Give an element a fixed size, a translucent border, one contrasting border-top-color and border-radius: 50%, then animate it with @keyframes rotating to 360 degrees, repeated infinitely. Animating only transform keeps it on the compositor, so it stays smooth while the page is busy loading data.
Understanding your result
CSS animations run on the compositor when they only change transform and opacity, which is why these loaders stay smooth even while the main thread is busy fetching data — precisely when a loader needs to keep moving. Two details matter for quality. First, accessibility: a spinner conveys state, so wrap it in an element with role="status" and aria-live="polite" plus visually hidden text such as "Loading…", or screen-reader users get no signal at all. Second, motion sensitivity: honour the prefers-reduced-motion media query by slowing the animation substantially or swapping in a static indicator, since continuous spinning can trigger discomfort. As a rule of thumb, show a spinner only after about 400ms of waiting — flashing one for a fast response feels worse than showing nothing.
Formula and method
Each loader combines a shaped element — a bordered circle, a filled dot or a bar — with a @keyframes rule that animates transform or opacity over a fixed duration, repeated infinitely. The spinning ring, for example, gives a circle one coloured border edge and rotates it a full 360 degrees per cycle.
Assumptions and limitations
These are indeterminate loaders: they show that something is happening but cannot show how far along it is, so use a real progress bar when the percentage is known. The generated CSS uses a .loader class name, which you may need to rename to avoid clashing with existing styles.
Worked example
A 48px ring in #2563eb with a 5px stroke and a one-second cycle produces a circle with a translucent blue track, a solid blue top edge and an animation that rotates it once per second.
How to use this tool
- Pick a loader style.
- Set the size, colour and cycle duration to match your interface.
- Copy the generated markup and CSS.
- Paste both into your project and show the element while loading.
Common mistakes to avoid
- Copying the CSS but forgetting the @keyframes block, so nothing animates.
- Leaving no accessible text, so screen readers announce nothing.
- Ignoring prefers-reduced-motion for users who need less animation.
- Flashing a loader for requests that finish in under a few hundred milliseconds.
About the CSS Loader Generator
The CSS Loader Generator builds loading animations from CSS alone — no JavaScript, no GIFs, no external requests. Choose from six styles, adjust size, colour, stroke thickness and cycle duration, and copy the markup and keyframes together.
Who should use this tool
Front-end developers who need a lightweight loading indicator without pulling in a library.
Benefits
- Pure CSS — nothing to download, so the loader appears instantly.
- Six distinct styles, from a classic ring to an indeterminate progress bar.
- Derives a translucent track colour automatically from your chosen colour.
- Copies the markup and the keyframes together, so it works when pasted.
Practical use cases
- Showing progress while an API request completes.
- Adding a spinner to a form submit button.
- Displaying a skeleton loading state on a dashboard.
- Replacing an animated GIF loader with something crisp on any screen.
Explore all Developer Tools tools
Frequently asked questions
Do these loaders need JavaScript?
No. They are pure CSS, so they animate as soon as the stylesheet parses — even while JavaScript is still loading or the main thread is blocked.
How do I make a loader accessible?
Wrap it in an element with role="status" and aria-live="polite", and include visually hidden text such as "Loading…" so assistive technology announces the state.
Are CSS loaders better than animated GIFs?
Generally yes. They add no network request, stay sharp at any resolution, support transparency cleanly and can be recoloured with a single property.
How do I respect reduced-motion preferences?
Wrap the animation in a @media (prefers-reduced-motion: reduce) query that lengthens the duration considerably or replaces the spinner with a static indicator.
When should the loader appear?
After roughly 400ms of waiting. Showing one immediately makes fast responses feel janky, because the spinner flashes on and straight back off.