State:
Loading…
Advertisement
FamilySpinner
Size40px
Duration800ms
Easinglinear
StateLoading
✓ 4.3:1

About this generator

Most CSS loader generators give you a single static spinner — change the color, copy the code, ship it. Then the QA engineer notices the screen reader stays silent during loading, the loader is invisible on the actual page background, the operation that takes 3 seconds feels slow because it shows a spinner instead of a skeleton, and the EU EAA compliance audit flags two failures. This generator was built to fix exactly that gap.

Eight loader families — spinner, dots, bars, pulse, skeleton, progress, dual-ring, cube — live in one editor with a family-picker grid. Each family signals a different UX promise (spinner for < 5 second actions, skeleton for content fetches, progress bar for determinate operations) and the generator helps you pick the right one. Twenty-four production presets — Stripe, Vercel, Linear, GitHub, Facebook skeleton, LinkedIn skeleton, iOS Activity, Material Circular, Discord typing, Tailwind UI, shadcn/ui — give you a starting point that’s already been validated against millions of users.

Three more differences worth calling out. Background-aware preview — pick the actual page background your loader will sit on (white, dark, brand, mesh) so you can see whether the contrast holds and the skeleton shimmer reads. WCAG-aware export — every snippet ships role=“status” + aria-live=“polite” + a screen-reader caption + an optional prefers-reduced-motion guard, automatically. Live 3:1 loader-vs-background contrast check (WCAG 1.4.11). Multi-state preview — click through Loading / Success / Error / Idle to see the transition; most generators freeze one state and call it done. Framework export — copy plain CSS, Tailwind arbitrary-value utilities, SCSS variables, React + TypeScript, Vue scoped, Svelte, or Astro snippet.

Permalink: every adjustment encodes into the URL hash, so you can bookmark a tuned loader or send it to a teammate. No login, no watermark, no paywall. Free and MIT-licensed.

?

How to use

01
Pick a family
Eight families up top — Spinner, Dots, Bars, Pulse, Skeleton, Progress, Dual-ring, Cube. Each maps to a different content + UX pattern; the panel below tells you when to pick which.
02
Start from a preset
Click any of the 24 production presets (Stripe Blue, Vercel Dots, Linear Pulse, Facebook Skeleton, LinkedIn Skeleton, iOS Activity, Material Circular, Discord Typing, Tailwind UI, shadcn/ui). They auto-filter to your active family. Customize from there.
03
Set size + color
Drag the size slider (px/em/rem unit toggle). For ring families, set stroke thickness. Choose Solid (one color), Two-tone (track + indicator), Gradient (animated @property typed angle), or CSS variable (theme-aware var(--accent)).
04
Pick the right page background
Critical for skeleton loaders — the shimmer is invisible against the wrong background. Click a preset (Dark / Light / Card / Brand / Mesh) or use the custom color picker. The contrast badge below the preview warns if the indicator falls under the 3:1 WCAG 1.4.11 minimum.
05
Check the success / error / idle states
Click the state pills (Loading / Success / Error / Idle) to preview the transition. A loader that never resolves to a check or X looks broken in production — most generators ignore this entirely.
06
Tune timing + accessibility
Duration / easing (including the new linear(...) ultra-smooth curve) / direction (normal / reverse / alternate). Open the Accessibility panel to set the aria-label, toggle prefers-reduced-motion fallback, and reserve box height for CLS-safe loading.
07
Export to your stack
Pick CSS, Tailwind arbitrary-value utilities, SCSS variables, React + TS, Vue scoped, Svelte, or Astro snippet. The output always ships role="status" + aria-live="polite" + a screen-reader caption and optional prefers-reduced-motion guard.
</>

Common loader snippets

Spinner (default)
<!-- HTML -->
<div class="loader-wrap" role="status" aria-live="polite" aria-label="Loading">
  <div class="loader"></div>
  <span class="loader-sr-only">Loading…</span>
</div>

/* CSS */
.loader-wrap { display: inline-flex; align-items: center; justify-content: center; }
.loader-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.loader {
  width: 40px; height: 40px;
  border-radius: 50%;
  border: 4px solid rgba(99,102,241,0.18);
  border-top-color: #6366f1;
  animation: spin 800ms linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
Skeleton card
<!-- HTML — content placeholder -->
<div class="loader-wrap" role="status" aria-live="polite" aria-label="Loading content">
  <div class="loader">
    <div class="skel skel-img"></div>
    <div class="skel skel-line" style="width:85%"></div>
    <div class="skel skel-line-sm" style="width:60%"></div>
  </div>
  <span class="loader-sr-only">Loading content…</span>
</div>

/* CSS — animated shimmer sweep */
.skel { background: #242526; border-radius: 6px; position: relative; overflow: hidden; }
.skel::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, #3a3b3c, transparent);
  transform: translateX(-100%);
  animation: skel-shimmer 1400ms ease-in-out infinite;
}
@keyframes skel-shimmer { to { transform: translateX(100%); } }
Reduced-motion fallback
@media (prefers-reduced-motion: reduce) {
  .loader, .loader *, .loader::before, .loader::after {
    animation-duration: 1.5s !important;
    animation-iteration-count: 1 !important;
  }
}
Accessible markup
<!-- The full WCAG-compliant loader pattern. -->
<div role="status" aria-live="polite" aria-label="Saving changes">
  <div class="loader"></div>
  <span class="sr-only">Saving changes…</span>
</div>

<!-- For ERROR states, switch role + aria-live aggressiveness: -->
<div role="alert" aria-live="assertive">
  Save failed — retry?
</div>

<!-- For LONG operations (>10s), provide progress + ETA: -->
<div role="progressbar" aria-valuenow="65" aria-valuemin="0" aria-valuemax="100"
     aria-label="Uploading hero-image.png">
  65% — about 12 seconds remaining
</div>
Tailwind v3 / v4
<!-- Tailwind v3/v4 arbitrary-value utilities. -->
<div role="status" aria-live="polite" aria-label="Loading">
  <div class="inline-block w-[40px] h-[40px] rounded-full
       border-[4px] border-transparent border-t-[#6366f1]
       animate-[spin_800ms_linear_infinite]"></div>
  <span class="sr-only">Loading…</span>
</div>

/* Add once in your global stylesheet: */
@keyframes spin { to { transform: rotate(360deg); } }

Browser support

Every property the generator emits — CSS animations, transforms, conic-gradient, @property typed angles, mask-image, prefers-reduced-motion — is supported in every evergreen browser. @property + animated conic-gradient fall back gracefully via @supports in Firefox < 128. Safe to ship anywhere.

Chrome v85+
Safari v16+
Firefox v128+
Edge v85+

Pro tips

01
Spinner vs Dots vs Skeleton — they're not interchangeable
<strong>Spinner / Dots / Pulse</strong> for actions under 5 seconds (form submit, navigation). <strong>Skeleton</strong> for content fetches over 1 second where you can hint at the final layout (feed posts, cards, profile pages). <strong>Progress bar</strong> for determinate operations where you can show real percentage (file upload, multi-step deploy). Picking the wrong family makes a fast app feel slow — wrong feedback signals false expectations.
02
Skeleton loaders need shimmer to read as "loading"
A static gray block reads as "broken layout" — the shimmer sweep is what signals "content coming." Default shimmer speed is 1.4-1.5s; slower than 2.5s feels stuck, faster than 800ms feels frenetic. Facebook + LinkedIn + YouTube all converged on ~1.4s after years of A-B testing.
03
Every loader MUST announce itself to screen readers
<code>role="status"</code> + <code>aria-live="polite"</code> + a visible-to-SR-only caption is the WCAG 2.2 compliant pattern. Without these, blind users hear silence while sighted users see motion. The generator outputs this combination automatically; lawsuits under EU EAA (June 2025), US Section 508, and UK Equality Act have specifically cited unannounced loaders.
04
Respect prefers-reduced-motion
Motion-sensitive users disable animation in their OS. Indefinite-loop spinners are textbook motion that can trigger nausea or migraine. The generator wraps every <code>@keyframes</code> in <code>@media (prefers-reduced-motion: reduce)</code> that slows the loop to 1.5s and stops iteration after one cycle — still visible as "something is happening" but stops short of continuous motion.
05
Reserve box height to prevent CLS
A loader that pops in mid-page-load shifts content. Set <code>min-height</code> on the loader's container (the generator emits this via the "Reserve box height" slider) so the layout reserves vertical space before the loader appears. CLS is one of three Core Web Vitals — every loader that ships without reserved height costs you a few hundredths of a point on every Lighthouse audit.
06
Don't use a spinner past 10 seconds
A spinning loader that runs for 10+ seconds reads as "stuck." Past 10 seconds, switch to a progress bar with a percentage (or estimated time remaining). For genuine unknown-duration operations over 10 seconds, ship a streaming-style indeterminate progress (the File Upload Streaming preset in this generator) so the user sees motion + progress messaging without false-precision percentages.

Search CodeFronts

Loading…