Navigation✨ Premium

Skeleton Card

Pure-CSS animated card placeholder: a low-opacity linear gradient sweeps in a loop to simulate a shimmer loading state — zero JavaScript, zero dependencies.

CSSLoadingShimmer

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Navigation has 36 effects, including 4 free. Explore the category →

3 usage examples

SaaS dashboard — Product card grid loading — Skeleton Card example 1

① SaaS dashboard — Product card grid loading

WhenThe user navigates to a dashboard section whose data is fetched via API — KPI cards, products, articles.
WhyThe skeleton reproduces the exact shape of the future cards (image + title + lines), preventing layout shift and anchoring attention during the request.
Settingsmax-width 280px, animation-duration 1.5 s. Instantiate multiple .skeleton-card side-by-side in a CSS grid to simulate a realistic list of 3 to 6 cards.
Article feed — Blog or news with lazy loading — Skeleton Card example 2

② Article feed — Blog or news with lazy loading

WhenInfinite scroll loads a new batch of articles: the skeleton displays during the request and gives way to real content once received.
WhyThe continuous shimmer clearly signals the page is active — users wait patiently rather than suspecting a bug or slow network.
SettingsStack 3–6 .skeleton-card vertically (max-width 100%). Reduce .skeleton-image to 80 px for compact lists with a horizontal thumbnail.
Video gallery — Media library or async course catalogue — Skeleton Card example 3

③ Video gallery — Media library or async course catalogue

WhenVideo thumbnails and metadata are loaded from a CDN or external API (YouTube, Vimeo, internal service).
WhyThe skeleton's image-to-text ratio naturally matches video thumbnails, making the loading-to-content transition smooth with no visual jump.
SettingsAdjust .skeleton-image to 157 px (16:9 ratio at 280 px wide) and trim to 2 lines (remove the third .skeleton-line) for short titles.

How it works

The shimmer relies on a single @keyframes shimmer rule that animates background-position from 200% 0 to -200% 0. Each skeleton zone's background is a linear-gradient(90deg) with very low-opacity stops (5% · 10% · 5%) and background-size: 200% 100% — as the position advances, the bright central band sweeps the surface left to right with no engine-side calculation.

Three zones reproduce a content card's anatomy: image (120 px height, border-radius: 8px), title (70% width, 16 px height), and text lines (100% · 90% · 60% width). Unequal proportions avoid a rigid grid feel and mimic the real typography of a product card or article.

The three lines carry staggered animation-delay values (0.1 s · 0.2 s · 0.3 s), creating a visible phase offset: the light appears to travel top-to-bottom rather than flashing simultaneously across the whole component.

The animation runs at a fixed duration of 1.5 s, looping infinite, with timing-function: ease. No JavaScript: the component is fully self-contained in CSS and integrates as-is into React, Vue, Angular, or Svelte without any initialisation.

Accessibility

  • prefers-reduced-motion absent: the code contains no @media (prefers-reduced-motion: reduce) rule. For users sensitive to continuous animation, add in your own stylesheet: .skeleton-image, .skeleton-title, .skeleton-line { animation: none; } guarded by the media query.
  • Skeleton elements are empty <div>s — add aria-hidden="true" on .skeleton-card and aria-live="polite" on the parent container so screen readers announce the state change once real content is injected.
  • The skeleton carries no interactive role and no focusable element: no keyboard navigation issue is inherent to the component.
  • Very low opacities (5% / 10%) are intentional — the skeleton is a placeholder, not content; it does not reach WCAG contrast thresholds, which is expected.
  • Indefinite duration: if content never loads, the animation loops forever — respect WCAG 2.2.2 by exposing a mechanism to stop or dismiss the skeleton after a reasonable timeout.

Browser compatibility

Relies solely on @keyframes, linear-gradient, and animated background-position — available in all modern browsers since 2015.

Chrome 49+✓ Full
Firefox 46+✓ Full
Safari 10+✓ Full
Edge 79+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without CSS animation support, skeleton zones display as static low-opacity blocks — no JavaScript is required, no error occurs.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="skeleton-card">
  <div class="skeleton-image"></div>
  <div class="skeleton-content">
    <div class="skeleton-title"></div>
    <div class="skeleton-text">
      <div class="skeleton-line"></div>
      <div class="skeleton-line"></div>
      <div class="skeleton-line"></div>
    </div>
  </div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
animation-duration (1.5 s) 1.5s Speed of the light sweep across all skeleton elements. Increase to 2.5 s for a softer feel, decrease to 0.8 s for a faster perceived load.
.skeleton-card max-width 280px Maximum card width. Set to 100% for a full-width card or adapt to your grid column size.
.skeleton-image height 120px Height of the image placeholder block. 157 px ≈ 16:9 ratio at 280 px wide, 280 px = perfect square.
gradient opacity stops .05 / .10 / .05 Opacity of the rgba(255,255,255,…) stops. Raise to .10 / .20 / .10 for a more visible shimmer; on light backgrounds invert to rgba(0,0,0,…).
.skeleton-title width 70% Width of the title placeholder bar. Range from 40% to 85% depending on the typical length of your real titles.
animation-delay per line .1s / .2s / .3s Phase offset between lines. Increase to .2 s / .4 s / .6 s for a more pronounced cascade; set to 0 s on all for a synchronised shimmer.
border-radius .skeleton-card 12px Card corner radius. 0 px for a full-width list layout, 16–20 px for a floating card style.

FAQ

Remove (or hide with display: none) the .skeleton-card element and inject the real card's HTML in its place. Add a fade-in transition (opacity 0→1 over 200 ms) on the real card for an invisible handoff.
Yes — skeleton-* classes are shared. Include the CSS once in your page or bundle, then instantiate as many .skeleton-card elements as needed. In React or Vue, create a <SkeletonCard /> component that renders only the skeleton HTML with no additional style.
The default gradient is calibrated for a dark background (#0a0a0f). On a light background, invert the stops: replace rgba(255,255,255,.05) with rgba(0,0,0,.05) and rgba(255,255,255,.10) with rgba(0,0,0,.12), and set .skeleton-card background to rgba(0,0,0,.04).