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.
<!-- 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); } } <!-- 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%); } } @media (prefers-reduced-motion: reduce) {
.loader, .loader *, .loader::before, .loader::after {
animation-duration: 1.5s !important;
animation-iteration-count: 1 !important;
}
} <!-- 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 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); } } 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.