← Back to gallery
CSS

Reduced Motion Skeleton Fallback

Skeleton loaders that swap a sweeping shimmer for a static placeholder or subtle opacity pulse when prefers-reduced-motion is reduce, keeping loading feedback clear.

Duration
3s
Resolution
1440×900
Format
CSS
skeletonshimmerprefers-reduced-motionaccessibilityplaceholder

Loader / prefers-reduced-motion / skeleton

Reduced motion skeleton fallback

Reimplements a skeleton loader in three motion tiers: default shimmer, reduced-motion opacity pulse, and a strict static placeholder — all reserving layout to prevent CLS on content swap.

default shimmershimmer

Motion-allowed path

Default Shimmer

The default path shows a moving linear-gradient sheen across skeleton blocks — the typical loading affordance for users without a reduced-motion preference.

  • default
  • shimmer
  • motion-ok

Reduced-motion with opacity pulse

Subtle Fade

When prefers-reduced-motion is reduce, the shimmer is replaced with a low-amplitude opacity pulse that signals loading without lateral travel.

  • reduced-motion
  • opacity
  • low amplitude

Strictest reduced-motion fallback

Static Placeholder

The strictest fallback renders a static placeholder with no animation at all. Layout is reserved, aria-busy announces loading, and nothing moves.

  • static
  • aria-busy
  • CLS-safe

Reduced motion inspector

Default Shimmer

Mode
shimmer
Cycle
1.80s

Matches what users see on most sites. The gradient sweeps continuously until real content arrives, giving a clear "still loading" signal.

css
.skeleton {
  background: rgba(148, 163, 184, 0.18);
  border-radius: 6px;
  position: relative;
  overflow: hidden;
}

.skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg,
    transparent 20%,
    rgba(255, 255, 255, 0.12) 50%,
    transparent 80%);
  background-size: 200% 100%;
  animation: skeletonSweep 1.80s linear infinite;
}

@keyframes skeletonSweep {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton::after { display: none; }
  .skeleton { animation: skeletonFade 1.80s ease-in-out infinite alternate; }
  @keyframes skeletonFade {
    from { opacity: 1; }
    to   { opacity: 0.40; }
  }
}