16 CSS Pulse Animations11 / 16

Pure CSSMIT licensed

Skeleton Loader Card Pulse

Content-loading skeleton screens — the LinkedIn / Facebook / YouTube pattern for graceful data fetching. 3 stacked feed cards each with an avatar circle, author line, three text blocks, and a media thumbnail — all animated with a diagonal shimmer sweep that reads as "content is on its way." Neutral gray canvas (#f8fafc) with light gray placeholders (#e2e8f0) and a bright shimmer highlight (#f8fafc) sweeping diagonally.

Published

Live Demo
Try it

The code

<div class="pl-11">
  <header class="pl-11__head">
    <div class="pl-11__brand">
      <span class="pl-11__brand-logo" aria-hidden="true">◇</span>
      <span>Northlight</span>
    </div>
    <div class="pl-11__nav-links">
      <span class="pl-11__nav-item pl-11__nav-item--sel">Feed</span>
      <span class="pl-11__nav-item">Projects</span>
      <span class="pl-11__nav-item">Notifications</span>
    </div>
  </header>
  <section class="pl-11__feed" role="status" aria-live="polite" aria-label="Loading 3 feed items">
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--mid"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--short"></div>
      <div class="pl-11__sk pl-11__sk--thumb"></div>
    </article>
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--mid"></div>
    </article>
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--short"></div>
      <div class="pl-11__sk pl-11__sk--thumb"></div>
    </article>
  </section>
</div>
.pl-11 {
  --page-bg: #f8fafc;
  --card: #ffffff;
  --ink: #0f172a;
  --sub: #64748b;
  --placeholder: #e2e8f0;
  --shimmer: #f1f5f9;
  --shimmer-highlight: #ffffff;
  --line: #e2e8f0;
  font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 16px;
  padding: 24px;
  background: var(--page-bg);
  color: var(--ink);
}

.pl-11 *,
.pl-11 *::before,
.pl-11 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.pl-11 ::selection {
  background: #0ea5e9;
  color: #fff;
}

.pl-11__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-radius: 11px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(15,23,42,.05);
}

.pl-11__brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: .9rem;
  letter-spacing: -.01em;
}

.pl-11__brand-logo {
  color: #0ea5e9;
  font-size: 1.1rem;
}

.pl-11__nav-links {
  display: flex;
  gap: 4px;
}

.pl-11__nav-item {
  padding: 5px 12px;
  border-radius: 6px;
  font-size: .78rem;
  font-weight: 600;
  color: var(--sub);
  cursor: pointer;
  transition: background .15s ease,color .15s ease;
}

.pl-11__nav-item:hover {
  background: #f1f5f9;
  color: var(--ink);
}

.pl-11__nav-item--sel {
  background: #f1f5f9;
  color: var(--ink);
}

.pl-11__feed {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(560px,100%);
  justify-self: center;
}

.pl-11__card {
  padding: 16px;
  border-radius: 11px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(15,23,42,.05);
}

.pl-11__card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.pl-11__card-meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
/* Skeleton primitive — gradient shimmer sweep */

.pl-11__sk {
  border-radius: 5px;
  background: linear-gradient(100deg,var(--placeholder) 20%,var(--shimmer-highlight) 50%,var(--placeholder) 80%);
  background-size: 200% 100%;
  background-position: -100% 0;
  animation: pl-11-shimmer 1.6s linear infinite;
}

@keyframes pl-11-shimmer {
  0% {
    background-position: -100% 0;
  }

  100% {
    background-position: 200% 0;
  }
}

.pl-11__sk--avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pl-11__sk--name {
  height: 12px;
  width: 35%;
  border-radius: 3px;
}

.pl-11__sk--stamp {
  height: 10px;
  width: 22%;
  border-radius: 3px;
}

.pl-11__sk--text {
  height: 11px;
  border-radius: 3px;
  margin-bottom: 8px;
}

.pl-11__sk--wide {
  width: 100%;
}

.pl-11__sk--mid {
  width: 80%;
}

.pl-11__sk--short {
  width: 55%;
}

.pl-11__sk--thumb {
  width: 100%;
  height: 140px;
  border-radius: 8px;
  margin-top: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .pl-11__sk {
    animation: none!important;
    background: var(--placeholder)!important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Pulse Animation from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Skeleton Loader Card Pulse
Source: https://codefronts.com/motion/css-pulse-animation/skeleton-loader-card-pulse/

Content-loading skeleton screens — the LinkedIn / Facebook / YouTube pattern for graceful data fetching. 3 stacked feed cards each with an avatar circle, author line, three text blocks, and a media thumbnail — all animated with a diagonal shimmer sweep that reads as "content is on its way." Neutral gray canvas (#f8fafc) with light gray placeholders (#e2e8f0) and a bright shimmer highlight (#f8fafc) sweeping diagonally.
## HTML
```html
<div class="pl-11">
  <header class="pl-11__head">
    <div class="pl-11__brand">
      <span class="pl-11__brand-logo" aria-hidden="true">◇</span>
      <span>Northlight</span>
    </div>
    <div class="pl-11__nav-links">
      <span class="pl-11__nav-item pl-11__nav-item--sel">Feed</span>
      <span class="pl-11__nav-item">Projects</span>
      <span class="pl-11__nav-item">Notifications</span>
    </div>
  </header>
  <section class="pl-11__feed" role="status" aria-live="polite" aria-label="Loading 3 feed items">
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--mid"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--short"></div>
      <div class="pl-11__sk pl-11__sk--thumb"></div>
    </article>
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--mid"></div>
    </article>
    <article class="pl-11__card">
      <header class="pl-11__card-head">
        <div class="pl-11__sk pl-11__sk--avatar"></div>
        <div class="pl-11__card-meta">
          <div class="pl-11__sk pl-11__sk--name"></div>
          <div class="pl-11__sk pl-11__sk--stamp"></div>
        </div>
      </header>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--wide"></div>
      <div class="pl-11__sk pl-11__sk--text pl-11__sk--short"></div>
      <div class="pl-11__sk pl-11__sk--thumb"></div>
    </article>
  </section>
</div>
```
## CSS
```css
.pl-11 {
  --page-bg: #f8fafc;
  --card: #ffffff;
  --ink: #0f172a;
  --sub: #64748b;
  --placeholder: #e2e8f0;
  --shimmer: #f1f5f9;
  --shimmer-highlight: #ffffff;
  --line: #e2e8f0;
  font-family: -apple-system,BlinkMacSystemFont,'Inter','Segoe UI',sans-serif;
  width: 100%;
  min-height: 100vh;
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: 16px;
  padding: 24px;
  background: var(--page-bg);
  color: var(--ink);
}

.pl-11 *,
.pl-11 *::before,
.pl-11 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.pl-11 ::selection {
  background: #0ea5e9;
  color: #fff;
}

.pl-11__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-radius: 11px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(15,23,42,.05);
}

.pl-11__brand {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 700;
  font-size: .9rem;
  letter-spacing: -.01em;
}

.pl-11__brand-logo {
  color: #0ea5e9;
  font-size: 1.1rem;
}

.pl-11__nav-links {
  display: flex;
  gap: 4px;
}

.pl-11__nav-item {
  padding: 5px 12px;
  border-radius: 6px;
  font-size: .78rem;
  font-weight: 600;
  color: var(--sub);
  cursor: pointer;
  transition: background .15s ease,color .15s ease;
}

.pl-11__nav-item:hover {
  background: #f1f5f9;
  color: var(--ink);
}

.pl-11__nav-item--sel {
  background: #f1f5f9;
  color: var(--ink);
}

.pl-11__feed {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: min(560px,100%);
  justify-self: center;
}

.pl-11__card {
  padding: 16px;
  border-radius: 11px;
  background: var(--card);
  border: 1px solid var(--line);
  box-shadow: 0 1px 3px rgba(15,23,42,.05);
}

.pl-11__card-head {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.pl-11__card-meta {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
}
/* Skeleton primitive — gradient shimmer sweep */

.pl-11__sk {
  border-radius: 5px;
  background: linear-gradient(100deg,var(--placeholder) 20%,var(--shimmer-highlight) 50%,var(--placeholder) 80%);
  background-size: 200% 100%;
  background-position: -100% 0;
  animation: pl-11-shimmer 1.6s linear infinite;
}

@keyframes pl-11-shimmer {
  0% {
    background-position: -100% 0;
  }

  100% {
    background-position: 200% 0;
  }
}

.pl-11__sk--avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  flex-shrink: 0;
}

.pl-11__sk--name {
  height: 12px;
  width: 35%;
  border-radius: 3px;
}

.pl-11__sk--stamp {
  height: 10px;
  width: 22%;
  border-radius: 3px;
}

.pl-11__sk--text {
  height: 11px;
  border-radius: 3px;
  margin-bottom: 8px;
}

.pl-11__sk--wide {
  width: 100%;
}

.pl-11__sk--mid {
  width: 80%;
}

.pl-11__sk--short {
  width: 55%;
}

.pl-11__sk--thumb {
  width: 100%;
  height: 140px;
  border-radius: 8px;
  margin-top: 6px;
}

@media (prefers-reduced-motion: reduce) {
  .pl-11__sk {
    animation: none!important;
    background: var(--placeholder)!important;
  }
}
```

How this works

Each skeleton element uses a linear-gradient background at 100° with three color stops: light gray → bright shimmer → light gray. The background-size is set to 200% of the element width, so the gradient extends beyond the visible area. A @keyframes pl-11-shimmer animates the background-position from -100% 0 to 200% 0 over 1.6 seconds — the shimmer sweeps left-to-right across every skeleton element in perfect sync. Because background-position is compositor-safe on modern browsers, the animation stays 60fps even with 20+ skeleton elements on one page.

All skeleton elements share the SAME animation reference (all use animation: pl-11-shimmer 1.6s linear infinite) — this means the shimmer aligns across the card, creating a coherent "the whole card is loading" signal rather than 15 independent glitching pieces. If you offset the shimmer with staggered delays, users perceive chaos instead of loading.

Neutral gray palette matches the platform-canonical skeleton screen: #e2e8f0 placeholder base, #f8fafc shimmer highlight, page background #f8fafc. Cards themselves are white (#ffffff) with soft shadow so users perceive the card container even while its contents load. Layout mimics a feed card exactly — avatar circle top-left, author line, timestamp, three text blocks, media thumbnail — so users know the shape of what's coming.

Semantic HTML uses role="status" and aria-live="polite" with aria-label="Loading feed items" so screen readers announce the loading state without reading the placeholder rectangles as content. When real content replaces the skeleton, screen readers announce the new content naturally.

Make it yours

  • Change shimmer speed by editing animation-duration: 1.6s. Slower (2s) feels calmer for read-heavy contexts (article previews). Faster (1.2s) feels more responsive for feed streams.
  • Adjust skeleton shape to match your real content — YouTube uses 16:9 video thumbnail + 2-line title, Twitter uses square avatar + inline text, Amazon uses product thumbnail + price. Match your actual content shape or the skeleton feels misleading.
  • For dark mode, invert colors: #1e293b placeholder base, #334155 shimmer highlight, #0f172a card background. Keep the same gradient technique.
  • Change gradient angle for different shimmer directions — 100° reads as "diagonal wipe left to right" (canonical), 90° is horizontal (looks like a curtain), 45° is bottom-left to top-right (feels more "scanning upward").
  • For accessibility, provide a text alternative in the skeleton state (aria-label="3 feed items loading") so screen readers announce it. Never rely on the visual skeleton for accessibility — screen-reader users see nothing until real content arrives.

Gotchas — read before shipping

  • The shimmer MUST be synchronized across all skeleton elements — don't stagger animation-delay. Offset shimmer looks like random glitching, not "content loading." Users perceive coordinated motion as one signal, staggered motion as chaos.
  • Do NOT use skeleton screens for loads under 200ms — the skeleton flashes and disappears, which is more jarring than a brief blank state. Show a spinner for < 200ms loads, skeleton for 200ms-2s, and a static loading page for anything longer than 2s.
  • For infinite feeds, do NOT show skeletons for every scroll load — the constant flashing is exhausting. Show a small spinner at the bottom, then swap in real content. Reserve skeleton screens for INITIAL page load only.
  • The skeleton shape must MATCH the incoming content — if you show 3 text lines and a thumbnail, the real content must have 3 text lines and a thumbnail. Mismatched skeletons feel like a bait-and-switch and erode trust.
  • For accessibility, wrap the skeleton in role="status" + aria-live="polite" + aria-label="Loading feed". Screen readers announce "Loading feed" once, not "gray box, gray box, gray box" for every placeholder.
  • Do NOT animate opacity as the skeleton loader — a fading skeleton looks like a broken UI. Use the gradient shimmer pattern instead: constant visibility with a moving highlight.
  • For prefers-reduced-motion: keep the skeleton VISIBLE in its static state (base placeholder color only, no shimmer). The loading indicator is informational — users still need to see "content is coming."

Browser support

ChromeSafariFirefoxEdge
45+10+40+45+

background-position + linear-gradient + @keyframes. Universal, GPU-accelerated.

Techniques used in this demo

Search CodeFronts

Loading…