15 CSS Shimmer Skeleton Cards

15 hand-coded CSS shimmer skeleton cards you can copy-paste into any project — Instagram-style profile cards with a spinning conic-gradient story ring, e-commerce product grids, data tables, SaaS dashboard widgets, blog article lists, social comment threads, search results, mobile horizontal feeds, form inputs, hero banners, chat bubbles, masonry galleries, dropdown notifications, and a dark-mode toggle skeleton. Every shimmer is a transform-only translateX(-100% → 100%) sweep on a GPU-composited ::after overlay — never background-position, which repaints every frame and kills INP on mobile. Zero CLS: every placeholder box reserves the exact dimensions of the real content it replaces, so the DOM never reflows when data arrives. Every demo ships aria-busy="true" + role="status" with a visually-hidden "Loading…" announcement for screen readers, and the sweep is disabled under prefers-reduced-motion: reduce. Scoped under .ssc-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

14 pure CSS1 light JSPublished

Related22 CSS Skeleton Loaders20 CSS Loaders25 CSS Spinners

Instagram-Style Profile Card — preview
01 / 15Pure CSS

Instagram-Style Profile Card

A social profile header skeleton on dark glass: a large avatar wrapped in a live conic-gradient story ring, beside staggered-width text lines and a follower-stats row. The frosted card floats on an oklch aurora mesh — proof that a loading state can carry brand personality instead of defaulting to gray-on-white.

Published

E-commerce Product Grid Card — preview
02 / 15Pure CSS

E-commerce Product Grid Card

A boutique three-up product grid skeleton: square image slots (aspect-ratio:1), a title line, a copper-tinted price bar and a button rectangle — each card framed by a gradient hairline border built with the background-clip trick. Warm oklch sand tones replace default gray, so the loading state already smells like the brand.

Published

Data Table Row — preview
03 / 15Pure CSS

Data Table Row

A terminal-dark table skeleton for admin dashboards: a header row of brighter pills above five zebra-striped rows of cyan-tinted cells, all sitting on a glass panel over a faint blueprint grid. The monospace-console aesthetic tells ops users "data is coming" without a single default gray.

Published

SaaS Dashboard Widget — preview
04 / 15Pure CSS

SaaS Dashboard Widget

KPI stat-card skeletons on an indigo aurora: three glass widgets, each with an icon slot, a metric label line, a tall electric-violet value bar and a slim trend line. The size hierarchy is deliberate — the value bar is visibly heavier and tinted with the accent, so users already know where the big number lands before the API answers.

Published

Blog Article List Item — preview
05 / 15Pure CSS

Blog Article List Item

An editorial media-object skeleton on warm ivory paper: a rectangular thumbnail with an inset hairline, beside three text lines at 80%, 100% and 40% — title, excerpt, metadata. The uneven widths are the whole trick: they read as natural paragraph text, and the sepia-tinted bars feel like a magazine, not a wireframe.

Published

Social Media Comment Section — preview
06 / 15Pure CSS

Social Media Comment Section

A threaded comment skeleton on soft lavender glass: each comment pairs a gradient-tinted circular avatar with a short name line and a two-line paragraph block, cascading in with staggered shimmer. The avatar sits aligned to the name's baseline zone — the micro-detail that separates a polished skeleton from an obviously fake one.

Published

Search Result Item — preview
07 / 15Pure CSS

Search Result Item

A SERP-anatomy skeleton with modern polish: a favicon dot + breadcrumb line, a large link-blue-tinted title bar, then a two-line snippet — with deliberately larger spacing between title block and snippet than between snippet lines. Ideal for site search, docs search and any query-driven list users scan vertically.

Published

Mobile Feed (Horizontal Scroll) — preview
08 / 15Pure CSS

Mobile Feed (Horizontal Scroll)

A stories-rail skeleton on near-black: an overflow-x scroller of square tiles, each wrapped in a sunset conic-gradient ring, with a caption line beneath. Tiles are flex-shrink:0 so they never squash, the rail scroll-snaps, and its edges fade out with a CSS mask — the premium overflow hint big social apps use.

Published

Simple Utility Card — preview
09 / 15Pure CSS

Simple Utility Card

The default, most reusable skeleton — reimagined: a 16:9 media slot whose surface carries a slow-breathing oklch gradient under the shimmer, a title line and two text blocks, inside a mint-glass card. Maps onto blog cards, link previews, docs tiles and pricing teasers with nothing but width tweaks.

Published

Form Input Placeholder — preview
10 / 15Pure CSS

Form Input Placeholder

A form skeleton for auth screens, checkout flows and settings pages: label lines followed by 42px input bars whose left edge carries a focus-ring-tinted accent, a taller textarea block and a gradient submit pill. The 42px height matches standard input controls exactly, so when the real form hydrates — or autofill fires — nothing moves a pixel.

Published

Hero Section Banner — preview
11 / 15Pure CSS

Hero Section Banner

A full-width hero skeleton with cinema presence: a massive banner slab breathing a dark aurora gradient, overlaid by centred headline, subline and CTA-pill placeholders swept by a wider, softer shimmer. This is the loading state for landing pages where the hero IS the first impression — it must feel designed, not broken.

Published

Chat Application Bubbles — preview
12 / 15Pure CSS

Chat Application Bubbles

A midnight messenger skeleton: incoming rows pair an avatar with a bubble cornered 18px 18px 18px 4px, outgoing rows float right with the mirrored 18px 18px 4px 18px radius and an accent-tinted fill. The asymmetric corners alone tell users which side is "them" before a single message arrives.

Published

Masonry Grid Gallery — preview
13 / 15Pure CSS

Masonry Grid Gallery

A Pinterest-style gallery skeleton in three CSS columns: tiles of varying heights (200, 350, 280px…) each washed with a different pastel oklch tint, so the loading wall already feels like a curated moodboard. Demonstrates how skeletons handle non-uniform content — the case simple grids can't fake.

Published

Dropdown Notification List — preview
14 / 15Pure CSS

Dropdown Notification List

A notification-tray skeleton as a floating dark glass dropdown — pointer notch included: a compact header row, then four tight rows of avatar circle + text line, one carrying an unread accent dot. Built with flex gap end-to-end so the rows stay crisply separated at trays of any density.

Published

Dark Mode Skeleton — preview
15 / 15CSS + JS

Dark Mode Skeleton

The Simple Utility Card's dark twin — theme-switchable live: the same 16:9 media slot, title and text blocks, but every colour flows from two custom properties (--sk-bg / --sk-shine) resolved through a data-theme attribute, with a real toggle button to flip light ↔ dark. On dark surfaces the shimmer must get SUBTLER, not brighter — this demo encodes that rule.

Published

FAQ

Frequently asked questions

What is a CSS shimmer skeleton and when should I use one?
A CSS shimmer skeleton is a placeholder version of a UI element (card, row, chat bubble, form field) that shows while the real content is fetching. It renders the same box dimensions as the final content and animates a diagonal light sweep across the placeholder so the user reads it as 'loading' instead of 'broken'. Use one whenever a fetch takes longer than ~200ms and the target box is a definable shape — profile cards, product grids, data tables, search results, chat threads, dashboard widgets. Every demo in this collection covers a specific real-world surface (Instagram profile, e-commerce grid, SaaS dashboard, Slack chat, etc.) so you can copy the closest match and retone the CSS custom properties to your brand. All 15 demos are graded on four axes: (1) ZERO CLS — every placeholder box is dimensionally identical to the real content it replaces, so the DOM never reflows when data arrives; (2) GPU-ACCELERATED — the shimmer is one transform: translateX(-100% → 100%) keyframe on an ::after overlay, composited by the browser (never animated background-position, which repaints on every frame); (3) ACCESSIBLE — the visual sits in aria-hidden pseudo-elements, and a visually-hidden 'Loading…' span inside a role='status' aria-busy='true' container announces the state to screen readers once; (4) REDUCED-MOTION — the sweep is disabled behind @media (prefers-reduced-motion: reduce), leaving a calm static placeholder that still communicates 'loading'.
How do I ship a skeleton loader without causing Cumulative Layout Shift (CLS)?
CLS happens when your skeleton's placeholder boxes are a different size than the real content that replaces them — the page reflows when the fetch resolves, everything below jumps, and Google's Core Web Vitals docks you for it (target CLS < 0.1). The fix is to measure your real content ONCE and hardcode those exact dimensions in the skeleton: a 72px avatar → 72px avatar circle; a 200-char product title → a two-line skeleton with the same line-height and max-width; a 42px input → a 42px skeleton block. Every demo in this collection reserves fixed widths and heights (or uses aspect-ratio for media slots) so the data swap shifts nothing. Bonus rule: never let the skeleton be TALLER than the real content — that shift is worse than 'shorter'. Test with Chrome DevTools' Performance panel → Layout Shifts overlay to catch drift after the fetch resolves. If your real content varies (comments, chat messages), ship a range of skeleton heights that MATCH the median plus P90 of your actual data — Slack, Discord, and Twitter all do this.
Why is transform-only shimmer faster than background-position animation?
Every frame of a background-position animation forces the browser to repaint the entire placeholder (the background is a paint property, not composited). At 60fps that's 16 repaints per second per skeleton, and on a page with 20 loading cards (product grid, comment thread, notification list) you're asking the CPU to repaint 320 boxes/second — mobile GPUs choke, INP drops, scroll janks. Transform animations run on the GPU compositor thread: the layer stays static and only its position on the composite is updated, which is nearly free. Every demo here builds the shimmer as a ::after pseudo-element containing a diagonal linear-gradient, positioned absolutely inside the placeholder, and animated with @keyframes { from { transform: translateX(-100%); } to { transform: translateX(100%); } }. The parent placeholder holds a static muted background; the ::after overlay sweeps across it. Confirmed on Chrome DevTools Performance panel: a page with 20 shimmer cards spends 0.3ms/frame in Painting versus 4-8ms for the background-position equivalent. That's the difference between smooth scroll on a $200 Android and dropped frames.
How do I make a skeleton loader accessible to screen readers?
Three rules, all shipped in every demo below: (1) MARK THE VISUALS DECORATIVE — every shimmer div, placeholder rectangle, and sweep overlay carries aria-hidden='true' so screen readers don't try to describe them (they'd otherwise announce a stream of empty divs). (2) ANNOUNCE THE STATE ONCE — wrap the skeleton container in role='status' aria-busy='true', and add a visually-hidden <span class='sr-only'>Loading …</span> inside so screen readers hear 'Loading' one time when the region appears (aria-live='polite' is implied by role='status'). (3) SWAP CLEANLY — when data arrives, remove aria-busy and let the real content render in place; if the real content has its own heading/list semantics, the screen reader picks up from there. Do NOT loop aria-live updates during the fetch — a skeleton shouldn't announce 'loading' every second, that's rude. Also never announce 'skeleton' — the user doesn't care about the placeholder pattern; they care that something is loading. The sr-only utility class is one declaration: position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;.
How do I ship an Instagram-style profile skeleton with the story ring?
Demo 01 is the reference implementation. Three techniques stacked: (1) STORY RING via conic-gradientbackground: conic-gradient(from 45deg, var(--ring-a), var(--ring-b), var(--ring-a)) on a 3px-padded parent, with the avatar circle inside a slightly smaller inset that masks the middle. Set --ring-a/--ring-b to the classic Instagram magenta/orange (oklch(0.72 0.19 350) / oklch(0.65 0.2 290)) or your brand's two-stop palette. (2) SPIN ANIMATION on the ring — the conic-gradient rotates via animation: spin 6s linear infinite where @keyframes spin { to { transform: rotate(360deg); } }, gated behind prefers-reduced-motion: no-preference. (3) SHIMMER on all internal placeholders — the avatar circle, the username row, the stats row — each carries the shared .ssc-01 .ssc-sk class that runs the transform-only sweep. When the real profile arrives, the outer container removes aria-busy, the ring stays (it's not a skeleton element, it's real UI), and the internal placeholders swap for the real avatar image + username text + stats. Every stat is a fixed-width flex child so the count changing from '—' to '1.2K' doesn't shift the row.
How do I convert these skeletons to React / Next.js Suspense fallbacks?
Every demo is framework-agnostic HTML + CSS + (optional) JS — copy the HTML into a Suspense fallback component and lift the CSS into a <style> block or your CSS Modules file. Next.js App Router pattern: create app/products/loading.tsx and return the skeleton JSX; Next automatically renders it while page.tsx is fetching. For a per-component fallback wrap the async component in <Suspense fallback={<ProductGridSkeleton />}>. Vue / Nuxt: use the <NuxtPage> transition prop or a v-if on your data. Svelte / SvelteKit: use {#await promise}<Skeleton />{:then data}...{/await}. In all frameworks, the skeleton component should reserve the same dimensions as the loaded component (see the CLS FAQ above) — measure your real component with DevTools first, then hardcode those into the skeleton. Because the shimmer is a pure CSS animation (no useEffect, no requestAnimationFrame), you avoid the class of React 'shimmer flicker' bugs where the animation restarts on every render — the sweep is owned by CSS and keeps its own timeline.
When should I use a skeleton loader vs a spinner vs a progress bar?
Rule of thumb: skeleton loaders are for CONTENT surfaces (list of cards, table of rows, thread of messages) where the user knows the general shape of what's coming; spinners are for OPAQUE work (form submit, file upload, payment processing) where nothing meaningful about the outcome can be previewed; progress bars are for KNOWN-DURATION work (upload of a 40MB file, running a 30-second calculation, an install with N of M steps done). Skeleton wins on perceived performance when the outcome is a scannable list — Instagram feed, Amazon product grid, Slack thread, dashboard widgets. Spinner wins for indeterminate opaque work (Stripe payment processing) — you can't preview the receipt because you don't know what it says yet. Progress bar wins when honest percentage math is available. NEVER use all three on the same page for the same fetch — pick one and be consistent. Also: a skeleton that shows for less than 200ms is worse than no skeleton (users perceive a flash of loading state instead of instant); wire a small setTimeout(showSkeleton, 200) so fast fetches skip the placeholder entirely. Facebook and Airbnb both ship this 200ms grace window.
How do I dark-mode a shimmer skeleton correctly?
Two rules: (1) INVERT THE TWO TOKENS not the whole design — every skeleton in this collection defines --sk-bg (the muted placeholder base) and --sk-shine (the sweep overlay). Light mode: --sk-bg: oklch(0.92 0.01 260); --sk-shine: rgba(255,255,255,.85). Dark mode: --sk-bg: oklch(0.28 0.015 280); --sk-shine: rgba(255,255,255,.07). Toggle the two tokens via [data-theme='dark'] or the light-dark() function (Demo 15 is the reference). Don't flip the shimmer to a dark stripe — a dark shimmer on a dark placeholder reads as a shadow, not a sweep. (2) THE SHINE'S OPACITY IS THE VOLUME KNOB — in dark mode, drop the shine opacity to ~7-10% because your eye is dark-adapted and any brighter feels aggressive. Light mode tolerates 80-85%. Test both modes with the shimmer running; if you can watch it for 10 seconds without flinching, the volume is right.

Related collections

20 CSS Animated Buttons preview

20 CSS Animated Buttons

20 hand-coded CSS animated buttons — neon glow, ripple, 3D press, liquid fill, jelly bounce, shine sweep, animated border, moving gradient CTA, text flip, submit success state, add-to-cart progress, download icon, hamburger-to-close, toggle switch, loading spinner inside button, next/prev arrow nav, and ghost button background reveal. Half pure CSS, half lightweight JS for production interactions.

11 CSS animation-timeline Demos preview

11 CSS animation-timeline Demos

11 pure CSS animation-timeline demos built on the W3C scroll() and view() spec — reading progress bar, view-timeline fade & reveal, shrinking sticky header, stacking cards, horizontal scroll section, parallax hero, container shadow indicators, reverse-scroll columns, text scrim reveal, cover card to sticky header, and image zoom / clip-path wipe. Zero JS, off the main thread, Core Web Vitals safe. Copy-paste ready.

25 CSS Background Animations preview

25 CSS Background Animations

25 hand-coded CSS background animations for SaaS hero sections, developer tool documentation, agency portfolios, gaming and Web3 landing pages, seasonal e-commerce campaigns, and dark-mode dashboards. Covers full-screen gradient shifts, animated mesh gradients, aurora borealis glow, cursor spotlight follow, interactive water ripples, dot grid and diagonal stripe patterns, SVG grain and noise texture overlays, topographic contour lines, layered SVG waves, plasma and lava lamp fluids, bokeh light blur, particle constellation networks, matrix digital rain, synthwave 3D perspective grids, starfield warp speed, CRT scanline overlays, fog and mist drift, morphing organic blobs, floating glassmorphism orbs, halftone dot patterns, starry night skies, floating geometric shapes, rising bubbles, and falling snow. 20 are pure CSS with zero JavaScript; the 5 that need it use vanilla canvas or pointer tracking with no dependencies. Every background animates compositor-only properties so it never blocks the main thread, is scoped under a .bga-NN prefix for no-collision pasting, guards prefers-reduced-motion, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

Search CodeFronts

Loading…