webdevpuneet.com

CSS Loader Gallery: 36 Pure CSS Spinners, Free HTML+CSS

CSS Loader Gallery · Loaders · Plain HTML, CSS & JS · Live preview

CategoryLoaders

Copy & paste

Source Code

<div class="wrap">
  <h2 class="title">CSS Loaders</h2>
  <p class="sub">36 pure CSS loaders, no JavaScript needed to animate them. Click one to copy its HTML and CSS together.</p>
  <div class="bar">
    <div class="chips" id="chips"></div>
    <div class="themes" id="themes"><span>Preview on</span><button type="button" data-theme="dark" class="on">Dark</button><button type="button" data-theme="light">Light</button></div>
  </div>
  <div class="gallery" id="gallery"></div>
  <div class="toast" id="toast" role="status"></div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

36 loaders in six families: spinners, dots, bars, shapes, skeletons and text
One click copies the HTML and the CSS together, as a block you can paste and run
Readable output: one declaration per line, keyframes indented, no minified strings
Recolour with --c and --track; a second accent, --c2, on the two-colour loaders
Dark and Light preview switch; the copied CSS is the same either way
Filter chips with live counts for each family
Staggered loaders drive their delays from an --i index with calc(), so a new dot is one new element
Skeleton loaders use an oversized gradient and a background-position sweep
No JavaScript animates any loader; the script only builds the grid, filters it and copies
Clipboard API with an execCommand fallback and a toast confirmation

About this UI Snippet

CSS Loader Gallery: Spinners, Dots, Bars, Skeletons and Text Loaders

Screenshot of the CSS Loader Gallery snippet rendered live

Every app needs a loading state, and most developers end up searching for the same thing: a small piece of CSS that spins, bounces or shimmers without a library. This gallery collects thirty-six of them in one place. There are ten spinners, six dot loaders, six progress and level bars, seven shapes, three skeleton placeholders and four text loaders, all animated with CSS @keyframes alone. Click any card and the clipboard receives the loader's HTML and its CSS together, as one block you can paste into a page and see working straight away.

What you get when you click

The copied block is the loader's markup followed by a <style> tag with its CSS, formatted one declaration per line so it is easy to read and edit. Nothing else from the gallery comes with it: no reset, no card styles, no script. A loader is usually one or two elements, so the paste is short. The markup comes along with the CSS, including the --i index variables that the staggered loaders use, so you never have to guess the structure.

Two variables recolour everything

Each loader declares its own custom properties on its root element: --c is the accent colour and --track is the pale colour behind it (the ring of a spinner, the rail of a progress bar, the base of a skeleton). Change --c to your brand colour and the loader follows, including the gradients that are mixed from it with color-mix(). The dual ring and ping pong loaders have a second accent, --c2. The Dark and Light switch above the grid previews the same CSS on both backgrounds, so you can see how a loader behaves on your own page before you copy it.

Six families, six techniques

Spinners rotate a bordered or conic-gradient element: the plain border-top spinner, a conic ring cut out with a mask, a dashed ring, a radar sweep and a clock with two hands on different durations. Dot loaders stagger one keyframe across several children with animation-delay, and the dot circle and wave loaders compute each delay from an --i index with calc(), so adding a ninth dot is one more element and no new rule. Bars scale or translate one fill inside a rail, and the striped bar slides a repeating-linear-gradient by exactly one stripe so it loops without a jump. Shapes use transforms and border-radius: a card flip with perspective, a morphing blob, a heart that beats, a square that becomes a circle. Skeletons sweep a highlight across an oversized gradient with background-position, which is the standard placeholder for cards, lists and images while data loads (see also the dedicated skeleton loader). Text loaders animate a caret, cycle the dots after a word with a content keyframe, shine a gradient through the letters, or hop each letter in turn.

Every loader and how it animates

The table lists all thirty-six, with the family each belongs to and what actually moves. Names match the cards above, and every row is a loader you can copy with one click.

LoaderFamilyHow it animates
SpinnerSpinnerA 3px border with a coloured top edge on a circle, rotated 360° every 0.8 s with linear timing.
Dual RingSpinnerDifferent border colours on the top and bottom of one circle, spinning at 0.9 s, so it reads as two arcs chasing each other.
OrbitSpinnerAn outer ring with two coloured edges (a ::before) spins around a fixed filled centre dot (an ::after).
Gradient RingSpinnerA conic-gradient that fades to transparent, cut into a ring with a radial mask and rotated once a second, so a tail trails the head.
Dashed RingSpinnerA dashed border on a circle turning slowly (3 s), so the dashes seem to march around it.
Double BounceSpinnerTwo translucent circles scale from 0 to 1 and back half a cycle apart over 2 s, so one grows as the other shrinks.
RadarSpinnerA circle holding a conic-gradient wedge that fades behind its leading edge, rotated every 1.4 s like a radar sweep.
Pulse RingSpinnerA ring scales from 0.6 to 1.4 while its opacity falls from 1 to 0, then restarts, like a ripple leaving a point.
RippleSpinnerTwo rings expand from a point and fade, the second delayed by half the cycle so a new wave starts as the last one ends.
ClockSpinnerA circle with two pseudo-element hands rotating about their base: the minute hand once a second, the hour hand once every 12 s.
Bouncing DotsDotsThree dots scale between 0.6 and 1 and fade, each 0.2 s after the one before, from one shared keyframe.
Typing DotsDotsThree small dots hop up 8 px in turn, 0.2 s apart: the familiar "someone is typing" bubble.
Wave DotsDotsFive dots swing between -9 px and +9 px, each delayed by 0.12 s times its --i index, so a wave travels along the row.
Dot CircleDotsEight dots set round a circle with rotate() and translateY(); each fades from full to faint, offset by one eighth of the cycle, so a bright dot appears to orbit.
Jumping BallDotsA ball drops 30 px and returns on a cubic-bezier ease while its shadow narrows and widens in step.
Ping PongDotsTwo dots of different colours slide the same 32 px in opposite directions (alternate and alternate-reverse) and cross in the middle.
EqualizerBarFive bars scaleY between 25% and 100% from their base, each starting 0.2 s later in the cycle, like an audio level meter.
Progress BarBarA 40%-wide gradient block slides across a rail with translateX, then wraps, for a task with no known percentage.
Striped BarBarDiagonal stripes from a repeating-linear-gradient; background-position moves by exactly one stripe period every 0.6 s, so the loop has no jump.
Signal BarsBarFour bars of rising height fade up one after another, 0.15 s apart, like a signal-strength or connecting indicator.
Fill BarBarA coloured fill grows from 0 to 100% of an overflow-hidden rail over 2 s, holds, then restarts.
Bar SweepBarA bar scales in from the left, then (by switching transform-origin halfway through the keyframes) scales out towards the right.
Grid FadeShapeNine squares fade in and out, each delayed by 0.1 s times its --i, so brightness travels across the grid.
Flip SquareShapeA square turns 180° about the X axis, then 180° about the Y axis, with perspective, for a card-flip rhythm.
Morphing BlobShapeborder-radius moves between two organic shapes while the element turns 90°, so the outline keeps changing.
HeartbeatShapeA heart built from a rotated square and two circles scales to 120% twice, then rests, like a pulse.
Square to CircleShapeA bordered square rotates 360° while shrinking to 60% and rounding its corners into a circle, then reverses.
Diamond SpinShapeA diamond turns through 360° while shrinking to about half size at the midpoint and growing back.
ChomperShapeTwo half-discs (a ::before and an ::after) rotate -32° and +32° about the centre line and back, so a mouth opens and closes.
Skeleton LinesSkeletonThree grey lines of different widths with a light highlight sweeping across an oversized gradient through background-position.
Skeleton CardSkeletonAn avatar circle and two text lines share one shimmer, so the placeholder reads as a profile row.
Shimmer BlockSkeletonA rounded rectangle with the same moving highlight, sized for an image or thumbnail placeholder.
Typing CursorTextA word followed by a 2px caret that blinks with step-end timing, as in a terminal.
Loading DotsTextThe ::after content cycles through nothing, ".", ".." and "..." with steps(1), so the dots appear one at a time.
Text ShimmerTextA gradient with a bright band is clipped to the letters (background-clip: text) and slid across them.
Hopping LettersTextEach letter jumps 8 px in turn, delayed by 0.1 s times its --i, so a hop runs along the word.

Pure CSS, and light on the page

None of the loaders runs JavaScript to animate. The only script in this page builds the gallery from one array, filters it by family and handles the copy button, so the loaders keep running in a page with scripts blocked, and they animate on the compositor wherever they use transform and opacity. Where a loader has to change layout, such as the fill bar that grows from zero to full width, the element is small and sits inside an overflow-hidden rail.

Using it

Pick a family with the chips, click a loader, paste the block, and change --c. For a button, scale the loader down with its own width and height or put it inside the button next to a label. For a page or panel, centre it in the container that is waiting for data and remove it when the request settles. Related patterns on this site include the dots loader, the progress bar and the loading overlay.

Step by step

How to Use

  1. 1
    Pick a familyUse the chips above the grid to show only spinners, dots, bars, shapes, skeletons or text loaders. The number on each chip is how many loaders it holds.
  2. 2
    Check it on your backgroundSwitch Preview on between Dark and Light. The loader's CSS does not change; only the pale track colour is swapped on the dark cards so you see how it will read on your page.
  3. 3
    Click to copy HTML and CSSClick a card. The clipboard receives the loader's markup followed by a <style> block with its CSS, and a toast names the loader that was copied.
  4. 4
    Paste it into your pagePaste the block where the loader should appear. It needs no other file: the markup is one to three elements and the CSS is self-contained, keyframes included.
  5. 5
    Recolour with --c and --trackChange --c on the loader's root element to your brand colour. Change --track if the loader sits on a dark surface. Dual Ring and Ping Pong also have --c2.
  6. 6
    Resize itChange the width and height on the root element. Loaders drawn with borders, gaps or pixel offsets may need those values adjusted in proportion at very different sizes.

Real-world uses

Common Use Cases

Add a loading state to a button or form
Put a small spinner or the typing dots inside a submit button while the request runs, then restore the label. Scale the loader to the button's line height and set --c to the button's text colour.
Show a placeholder while a feed or card list loads
The skeleton lines, card and block mirror the shape of the content that is coming, which keeps the layout steady. Repeat one three or four times, then replace them with the real items.
Indicate progress of an upload or a long task
The fill bar and striped bar look busy, and the progress bar and bar sweep move back and forth when there is no percentage. If you know the percentage, keep the rail and set the width from it.
CHAT
Show that someone is typing or an assistant is thinking
Typing dots, the loading dots text and the typing cursor are the usual patterns for a pending chat reply. Place one in the message list and remove it when the first characters arrive.

Got questions?

Frequently Asked Questions

The loader's HTML followed by a <style> block with its CSS, formatted one declaration per line. Paste the whole block into a page and the loader runs. It does not include the gallery's own styles or script, so there is nothing to clean up afterwards.

Edit the --c custom property on the loader's root element, for example .ld-spin{--c:#16a34a}. Where the loader has a pale background, such as the ring of a spinner or the rail of a bar, that is --track. Dual Ring and Ping Pong take a second colour in --c2. The pale defaults suit a light page; set --track to a darker tone on a dark one.

The copied CSS always carries the light-page defaults, so it is the same whichever preview you used. On the dark cards the gallery only overrides --track so the pale rings and rails do not glare against the card. If your page is dark, set --track yourself after pasting.

Each element carries an inline --i index (0, 1, 2 and so on) and the CSS computes animation-delay: calc(var(--i)*.12s). One keyframe is shared by every dot and the delay places each one on the cycle. To add a dot, add one more element with the next --i; to change the speed, change the multiplier.

Add one object to the loaders array with a type, a name, an html string and a css string that declares --c and --track on its root class. Give its classes and keyframes a unique ld- prefix so they cannot clash with the others. The grid, the chips and the copy output pick it up without any other change.