CSS Loader Generator

Free toolNo sign-up
State:
Loading…
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.

Chromev85+
Safariv16+
Firefoxv128+
Edgev85+

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.
?

Frequently asked questions

01

Why does this generator have 8 loader families when most have only 1 or 2?

Because picking the right loader for the job is more important than picking a pretty loader. Each family signals a different UX promise. Spinner for < 5 second actions (form submit, navigation) where you can't show progress. Dots for typing indicators and conversational pauses (Discord, Slack, iMessage, ChatGPT). Bars for audio / equalizer / playback contexts. Pulse for ambient background loading (notifications loading, presence indicators). Skeleton for content fetches over 1 second where you can hint at the final layout (Facebook feed, LinkedIn cards, YouTube grid, Twitter timeline) — the single biggest perceived-performance win in modern web UX. Progress bar for determinate operations with known percentage (file upload, multi-step deploy, software install). Dual-ring for premium / branded loading where the spinner is part of the visual identity (MUI Circular, Tailwind UI default, Linear's brand loader). Cube for playful / gaming / Web3 brands where a slight whimsy fits. Most generators ship one default spinner; this one gives you the actual UX vocabulary. Competitors like css-loaders.com curate ~600 styles but offer zero customization — you copy raw CSS. loading.io paywalls SVG and Lottie exports (~$59-149). spinkit is frozen at 2018. We give you live customization across all 8 families in one editor.

02

What makes the WCAG-aware export different from copying CSS off a free loader site?

Three things every other generator skips. 1. role="status" + aria-live="polite" + an sr-only caption — required by WCAG 2.2 and explicitly cited in EU EAA (June 2025 enforcement), US Section 508, UK Equality Act 2010, Canada ACA, and Australian DDA compliance lawsuits. Without these, blind users hear silence while sighted users see motion. Our export ships them automatically. 2. prefers-reduced-motion fallback — ~35% of adults have some level of motion sensitivity (vestibular disorders, migraine triggers, ADHD visual processing, PTSD photosensitivity). The generator wraps every @keyframes in a media query that slows the loop to 1.5s and stops iteration — still visible as "something is happening" but stops short of continuous motion. WCAG 2.3.3 explicitly requires this. 3. Live loader-vs-background contrast check — WCAG 1.4.11 (Non-text Contrast) requires a 3:1 ratio between graphic indicators and their background. A pale-gray spinner on a slightly-darker-gray background is the most common a11y bug in 2026 dashboards; the generator flags it before you ship. Lawsuits citing failed loader announcements have been filed against Domino's (Robles v Domino's, 2019), Target, Charles Schwab, and dozens of e-commerce sites under ADA in the US alone. EU EAA enforcement began June 2025; expect a similar wave.

03

Skeleton loader vs spinner — when should I use each?

Decision rule by content type and operation duration. Use a skeleton loader when: you can hint at the final layout (feed posts, profile cards, dashboard tables, blog post lists, product grids), the operation takes > 1 second, the content has a known structure. Skeleton loaders dramatically improve perceived performance because they reduce uncertainty — the user sees the final layout coming and unconsciously commits to waiting. Facebook reported a 50% reduction in "loading feels slow" complaints after switching from spinner to skeleton in 2018. LinkedIn, YouTube, Twitter, Slack, and Discord all follow the same pattern. Use a spinner when: the operation is < 5 seconds (form submit, navigation, single action), the result layout is unknown or unpredictable, the user has just clicked a button or link and needs immediate acknowledgment. Use a progress bar when: you can show real percentage (file upload, multi-step build, software install). Don't ever: use a spinner past 10 seconds (switch to progress bar with percentage or ETA), use a skeleton for instant operations (under 200ms — no loader at all is better), use multiple competing loaders on the same page (pick one signal, not three).

04

How does this compare to loading.io, css-loaders.com, spinkit, and react-loader-spinner?

loading.io: Lottie + SVG focus, well-designed presets but the CSS export is limited and most useful features ($59-149) sit behind a paywall. Push-traffic upsell. No accessibility content. css-loaders.com (Temani Afif): the de facto leader with 600+ pre-built loaders organized by family. Excellent curation. But there's zero customization — you copy CSS and edit by hand. No size/speed/color sliders. No framework export. No accessibility hints. No URL share. spinkit: 11 classic spinners with raw CSS source. Static page hasn't meaningfully updated since 2018. Copy-only, no customization. three-dots (nzbin): similar — 8 dot-loader variants as raw CSS. react-loader-spinner (npm, ~30K weekly downloads): 11 spinner components, React-only, adds ~6KB to your bundle. react-spinners (npm, ~700K weekly downloads): 22 spinner components, React-only, well-tested but locks you into React + the package's CSS-in-JS implementation. Tailwind UI / shadcn/ui / MUI CircularProgress / Chakra Spinner / Ant Spin / Mantine Loader: each framework ships one default spinner. Limited customization, no skeleton-loader, no progress-bar, no accessibility-aware export. This generator: 8 families, 24 presets, full live customization (size / color / timing / direction / accessibility), 7 framework exports, WCAG-aware output, zero bundle weight (pure CSS), MIT licensed, no login, no watermark.

05

Will the Tailwind export work with Tailwind v3 AND v4?

Yes. The generator emits Tailwind arbitrary-value utilities (square-bracket syntax like w-[40px] border-[#6366f1] animate-[spin_800ms_linear_infinite]) which both Tailwind v3 and v4 consume identically — no migration needed. For the @keyframes themselves (Tailwind doesn't ship custom keyframes out of the box), the export adds the keyframe definitions in a separate /* In your global stylesheet */ block that you paste once into your app.css or tailwind.css. Tailwind UI ($300+/yr/dev license) ships one default spinner pattern; this generator gives you 8 loader families with the same arbitrary-value approach. For users coming from shadcn/ui (free React + Radix), Magic UI, Aceternity UI, the React export is a drop-in CssLoader.tsx component with TypeScript props. No external dependencies, no Framer Motion required.

06

Why is the URL changing as I drag sliders?

Every adjustment is encoded into the URL hash (the part after #) so the entire loader config is shareable. Bookmark a tuned loader, reopen the URL, the loader is exactly where you left it. Send the URL to a colleague — they see your exact configuration. No accounts, no servers, no logins. The hash is base64-encoded JSON; you can inspect it in DevTools if you're curious. This is especially useful for design system documentation pages — link to the canonical loader URL from your design system docs and the live editor opens with your spec preloaded. The 24 presets are also URL-loadable so you can reference "our loader matches the Stripe Blue preset" with a single shareable link.

07

What's the difference between the linear(...) easing option and ease-in-out?

linear(...) is a 2024 CSS easing function (Chrome 113+, Safari 17.2+, Firefox 112+) that takes a sequence of progress values and interpolates linearly between them — letting you build any custom easing curve with precise control points. For loaders specifically, the killer use case is spring-bounce mimics without overshoot: linear(0, 0.25, 0.5, 0.75, 1) approximates a smooth in-out without the "snap-back" that cubic-bezier introduces on continuous loops. Practical impact on 120Hz displays: with variable refresh rate (VRR) screens (MacBook Pro M-series, iPad Pro, Pixel 8 Pro, ProMotion displays), traditional cubic-bezier easing can look juddery because the easing curve assumes a fixed 60fps frame budget. linear(...) with enough control points gives the browser interpolation freedom that adapts to the actual refresh rate. For most loaders < 1 second, the difference is invisible — for slow ambient loaders > 2 seconds, the smoother curve is noticeable. The generator's Easing dropdown includes both cubic-bezier(0.34, 1.56, 0.64, 1) (spring) and linear(0, 0.25, 0.5, 0.75, 1) so you can A/B them in your preview.

08

How do I prevent CLS (Cumulative Layout Shift) when a loader appears?

CLS is one of three Core Web Vitals Google measures on every Lighthouse / PageSpeed Insights audit and uses as a ranking signal. A loader that pops in mid-page-load shifts content below it, which spikes your CLS score. Two fixes baked into the generator. 1. Reserve box height: drag the "Reserve box height" slider in the Accessibility panel to set a min-height on the loader's container BEFORE the loader appears, so the layout reserves vertical space. The generator emits this as .loader-wrap { min-height: 80px; } on the wrapping element. 2. Render inline in initial HTML: the loader should appear in the server-rendered HTML, not be injected by JavaScript after hydration. For skeleton loaders specifically: the skeleton IS the layout reservation — when the real content arrives, the skeleton container is the exact same dimensions as the content, so layout shift is zero by construction. This is the second-biggest reason to use skeleton loaders over spinners for content fetches. Tools to measure: Chrome DevTools Performance Insights (Layout Shift detail), Lighthouse, PageSpeed Insights, Web Vitals Chrome extension, Core Web Vitals report in Google Search Console.

09

Why output a prefers-reduced-motion media query automatically?

Roughly 35% of adults experience some level of motion sensitivity — vestibular disorders, migraine triggers, ADHD-related visual-processing differences, PTSD photosensitivity. Indefinite-loop loaders (every spinner / dots / bars / pulse pattern) are textbook motion that can trigger nausea, dizziness, or headache. Users with these issues set prefers-reduced-motion: reduce in their OS (macOS Accessibility, Windows Animation, iOS Reduce Motion, Android Remove Animations). The generator wraps every @keyframes in a media query that slows the loop to 1.5s and stops iteration after one cycle — still visible as "something is happening" (you don't want to remove the loader entirely; users need feedback) but stops short of continuous motion that triggers symptoms. WCAG 2.3.3 (Animation from Interactions) explicitly recommends this. EU EAA (June 2025 enforcement), US Section 508, Canada ACA, UK Equality Act, Australian DDA all require WCAG 2.2 AA compliance — loader animations that don't honour prefers-reduced-motion are routine audit failures.

10

Will more features be added?

Yes. This is Tier 1 of the planned feature set: 8 loader families + 24 presets + multi-state preview + WCAG-aware export + 7 framework exports + URL hash persistence + reserved-height CLS prevention + contrast checker. Planned for v1.1: PNG / SVG / animated GIF export for static thumbnails and email-safe loaders (the loading.io paywall use case), OKLCH + Display-P3 color output for the 2026 modern color stack, scroll-driven loader (animation-timeline: scroll() pulling progress from scroll position), 12 more presets covering Notion / Figma / Vercel / Cloudflare patterns. Planned for v1.2: AI-suggested timing curves (Claude Haiku analyzes your brand's existing animations and matches), save-to-localStorage personal library, history with undo/redo, embed iframe code. Related: pair this generator with our CSS Loaders collection (curated production loaders), CSS Loading Animations, CSS Skeleton Loaders, and the CSS to Tailwind converter for converting generator output to your existing Tailwind config.

Search CodeFronts

Loading…