22 CSS Transition Effects 10 / 22

Loading Skeleton Transition

Shimmer, wave and pulse skeleton variants with toggle to reveal loaded content across card grid and data table layouts.

CSS + JS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="wrap">
  <h1>CSS Loading Skeleton</h1>
  <p class="sub">Shimmer, wave and pulse skeleton screens — pure CSS, no JS for the animations</p>

  <div class="toggle-wrap">
    <span class="toggle-label">Mode:</span>
    <button class="toggle-btn skeleton-mode" id="toggleBtn" onclick="toggleLoaded()">Showing skeleton → click to load</button>
  </div>

  <span class="sl">Card skeletons — shimmer variant</span>
  <div class="grid-3" id="cardGrid">
    <div class="skel-card state-panel">
      <div class="skeleton-state">
        <div class="skel skel-r skel-img"></div>
        <div class="skel-row"><div class="skel skel-round skel-avatar"></div><div class="skel-lines"><div class="skel skel-r skel-h1"></div><div class="skel skel-r skel-h2"></div></div></div>
        <div class="skel-tags"><div class="skel skel-tag"></div><div class="skel skel-tag"></div></div>
      </div>
      <div class="loaded-state">
        <div class="lc-img" style="--bg:linear-gradient(135deg,#7c3aed,#a78bfa)">🚀</div>
        <div class="lc-row"><div class="lc-avatar" style="--av:#7c3aed">M</div><div><div class="lc-name">Marcus Reid</div><div class="lc-role">Frontend Engineer</div></div></div>
        <div class="lc-tags"><span class="lc-tag" style="--tc:#7c3aed">CSS</span><span class="lc-tag" style="--tc:#0ea5e9">React</span></div>
      </div>
    </div>
    <div class="skel-card state-panel">
      <div class="skeleton-state">
        <div class="skel skel-r skel-img"></div>
        <div class="skel-row"><div class="skel skel-round skel-avatar"></div><div class="skel-lines"><div class="skel skel-r skel-h1"></div><div class="skel skel-r skel-h2"></div></div></div>
        <div class="skel-tags"><div class="skel skel-tag"></div><div class="skel skel-tag"></div></div>
      </div>
      <div class="loaded-state">
        <div class="lc-img" style="--bg:linear-gradient(135deg,#0ea5e9,#38bdf8)">🌊</div>
        <div class="lc-row"><div class="lc-avatar" style="--av:#0ea5e9">Y</div><div><div class="lc-name">Yuki Mori</div><div class="lc-role">Data Scientist</div></div></div>
        <div class="lc-tags"><span class="lc-tag" style="--tc:#0ea5e9">Python</span><span class="lc-tag" style="--tc:#10b981">ML</span></div>
      </div>
    </div>
    <div class="skel-card state-panel">
      <div class="skeleton-state">
        <div class="skel skel-r skel-img"></div>
        <div class="skel-row"><div class="skel skel-round skel-avatar"></div><div class="skel-lines"><div class="skel skel-r skel-h1"></div><div class="skel skel-r skel-h2"></div></div></div>
        <div class="skel-tags"><div class="skel skel-tag"></div><div class="skel skel-tag"></div></div>
      </div>
      <div class="loaded-state">
        <div class="lc-img" style="--bg:linear-gradient(135deg,#f97316,#fb923c)">🔥</div>
        <div class="lc-row"><div class="lc-avatar" style="--av:#f97316">A</div><div><div class="lc-name">Amara Cole</div><div class="lc-role">Product Designer</div></div></div>
        <div class="lc-tags"><span class="lc-tag" style="--tc:#f97316">Figma</span><span class="lc-tag" style="--tc:#ec4899">Motion</span></div>
      </div>
    </div>
  </div>

  <span class="sl">Table skeleton — wave variant</span>
  <div class="skel-table" style="margin-bottom:48px">
    <div class="skel-thead">
      <div class="skel skel-wave skel-round skel-avatar-sm"></div>
      <div class="skel skel-wave skel-cell"></div>
      <div class="skel skel-wave skel-cell-sm"></div>
      <div class="skel skel-wave skel-cell-sm"></div>
      <div class="skel skel-wave skel-cell-sm"></div>
      <div class="skel skel-wave skel-cell-sm"></div>
    </div>
    <div class="skel-tbody-row"><div class="skel skel-wave skel-round skel-avatar-sm"></div><div class="skel skel-wave skel-cell"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div></div>
    <div class="skel-tbody-row"><div class="skel skel-wave skel-round skel-avatar-sm"></div><div class="skel skel-wave skel-cell"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div></div>
    <div class="skel-tbody-row"><div class="skel skel-wave skel-round skel-avatar-sm"></div><div class="skel skel-wave skel-cell"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div></div>
    <div class="skel-tbody-row"><div class="skel skel-wave skel-round skel-avatar-sm"></div><div class="skel skel-wave skel-cell"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div><div class="skel skel-wave skel-cell-sm"></div></div>
  </div>
</div>
@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800&display=swap');
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:'Nunito',sans-serif;background:#0f0f1a;color:#e2e8f0;min-height:100vh;padding:60px 24px}
.wrap{max-width:1100px;margin:0 auto}
h1{font-size:clamp(2rem,5vw,3rem);font-weight:800;text-align:center;margin-bottom:8px;letter-spacing:-.03em}
.sub{text-align:center;color:#6b7280;margin-bottom:56px}
.sl{font-size:.68rem;font-weight:700;letter-spacing:.25em;text-transform:uppercase;color:#6b7280;margin-bottom:16px;display:block}
h2{font-size:1.3rem;font-weight:700;margin-bottom:24px}

/* THE SKELETON CORE */
.skel{background:linear-gradient(90deg,#1e1e2e 25%,#2a2a40 50%,#1e1e2e 75%);background-size:200% 100%;animation:trn10-shimmer 1.4s infinite}
@keyframes trn10-shimmer{to{background-position:-200% 0}}
.skel-r{border-radius:4px}
.skel-round{border-radius:50%}

/* variant 2 — wave on dark */
.skel-wave{background:linear-gradient(90deg,#1a1a2e 25%,#252540 50%,#1a1a2e 75%);background-size:400% 100%;animation:trn10-wave 1.8s ease-in-out infinite}
@keyframes trn10-wave{0%{background-position:100% 0}100%{background-position:-100% 0}}

/* variant 3 — pulse */
.skel-pulse{animation:trn10-pulse 1.5s ease-in-out infinite}
@keyframes trn10-pulse{0%,100%{opacity:.4}50%{opacity:.9}}

/* grid */
.grid-3{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;margin-bottom:48px}
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:24px;margin-bottom:48px}
@media(max-width:800px){.grid-3{grid-template-columns:1fr 1fr}}
@media(max-width:540px){.grid-3,.grid-2{grid-template-columns:1fr}}

/* SKELETON CARD */
.skel-card{background:#13132a;border-radius:16px;padding:20px;display:flex;flex-direction:column;gap:12px;border:1px solid rgba(255,255,255,.04)}
.skel-img{height:180px;border-radius:10px}
.skel-avatar{width:44px;height:44px;flex:0 0 44px}
.skel-row{display:flex;align-items:center;gap:12px}
.skel-lines{flex:1;display:flex;flex-direction:column;gap:8px}
.skel-h1{height:14px}
.skel-h2{height:12px;width:70%}
.skel-h3{height:12px;width:50%}
.skel-tag{height:24px;width:80px;border-radius:20px}
.skel-tags{display:flex;gap:8px}
.skel-btn{height:38px;border-radius:8px;width:120px}

/* ARTICLE skeleton */
.skel-article{background:#13132a;border-radius:16px;padding:28px;border:1px solid rgba(255,255,255,.04)}
.skel-title{height:28px;margin-bottom:8px}
.skel-title2{height:28px;width:70%;margin-bottom:24px}
.skel-para{height:12px;margin-bottom:8px}
.skel-para-short{height:12px;width:75%}

/* PROFILE skeleton */
.skel-profile{background:#13132a;border-radius:16px;padding:28px;display:flex;flex-direction:column;align-items:center;gap:16px;text-align:center;border:1px solid rgba(255,255,255,.04)}
.skel-avatar-lg{width:80px;height:80px;flex:0 0 80px;border-radius:50%}
.skel-name{height:18px;width:140px}
.skel-sub{height:12px;width:100px}
.skel-stat-row{display:flex;gap:24px;margin-top:8px}
.skel-stat{display:flex;flex-direction:column;align-items:center;gap:8px}
.skel-stat-n{height:22px;width:44px}
.skel-stat-l{height:10px;width:44px}
.skel-follow{height:40px;width:140px;border-radius:20px}

/* DATA TABLE skeleton */
.skel-table{background:#13132a;border-radius:16px;overflow:hidden;border:1px solid rgba(255,255,255,.04)}
.skel-thead{display:grid;grid-template-columns:44px 1.5fr 1fr 1fr 1fr 80px;gap:16px;padding:14px 20px;background:#1e1e2e;align-items:center}
.skel-tbody-row{display:grid;grid-template-columns:44px 1.5fr 1fr 1fr 1fr 80px;gap:16px;padding:14px 20px;align-items:center;border-top:1px solid rgba(255,255,255,.04)}
.skel-cell{height:12px;border-radius:4px}
.skel-cell-sm{height:12px;width:70%;border-radius:4px}
.skel-avatar-sm{width:32px;height:32px;border-radius:50%}
@media(max-width:600px){.skel-thead,.skel-tbody-row{grid-template-columns:44px 1fr 1fr}}

/* the toggle to see loaded state */
.toggle-wrap{display:flex;align-items:center;justify-content:center;gap:16px;margin-bottom:48px}
.toggle-label{font-size:.88rem;font-weight:600;color:#6b7280}
.toggle-btn{padding:10px 24px;border-radius:20px;border:none;font-family:'Nunito';font-weight:700;font-size:.88rem;cursor:pointer;transition:all .2s}
.toggle-btn.skeleton-mode{background:#7c3aed;color:#fff}
.toggle-btn.loaded-mode{background:#10b981;color:#fff}

/* loaded state cards (hidden by default) */
.loaded-card{background:#13132a;border-radius:16px;padding:20px;display:flex;flex-direction:column;gap:12px;border:1px solid rgba(255,255,255,.04);opacity:0;transition:opacity .4s}
.loaded-card.visible{opacity:1}
.lc-img{height:180px;border-radius:10px;background:var(--bg);display:grid;place-items:center;font-size:3rem}
.lc-row{display:flex;align-items:center;gap:12px}
.lc-avatar{width:44px;height:44px;border-radius:50%;background:var(--av);display:grid;place-items:center;font-size:1.2rem;flex:0 0 44px}
.lc-name{font-weight:700;font-size:.9rem}
.lc-role{font-size:.75rem;color:#9ca3af}
.lc-tags{display:flex;gap:8px;flex-wrap:wrap}
.lc-tag{font-size:.7rem;font-weight:700;padding:4px 10px;border-radius:16px;background:var(--tc);color:#fff}
.lc-title{font-weight:700;font-size:1rem;color:#f1f5f9}
.lc-desc{font-size:.82rem;color:#94a3b8;line-height:1.5}
.lc-btn{align-self:flex-start;padding:9px 20px;border-radius:8px;background:#7c3aed;color:#fff;font-family:'Nunito';font-weight:700;font-size:.82rem;border:none;cursor:pointer}

.state-panel{position:relative}
.state-panel .skeleton-state,.state-panel .loaded-state{position:absolute;inset:0;transition:opacity .4s}
.state-panel .skeleton-state{opacity:1}
.state-panel .loaded-state{opacity:0}
.state-panel.is-loaded .skeleton-state{opacity:0}
.state-panel.is-loaded .loaded-state{opacity:1}
.state-panel{min-height:280px}

@media (prefers-reduced-motion:reduce){.skel,.skel-wave,.skel-pulse{animation:none !important}}
function toggleLoaded(){
  const panels = document.querySelectorAll('.state-panel');
  const btn = document.getElementById('toggleBtn');
  const isLoaded = panels[0].classList.contains('is-loaded');
  panels.forEach(p => p.classList.toggle('is-loaded', !isLoaded));
  if(!isLoaded){
    btn.className='toggle-btn loaded-mode';
    btn.textContent='Showing loaded → click to show skeleton';
  } else {
    btn.className='toggle-btn skeleton-mode';
    btn.textContent='Showing skeleton → click to load';
  }
}

Search CodeFronts

Loading…