
Instagram-Style Profile Card
Published
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.

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published

Published
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'.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.::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.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;.conic-gradient — background: 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.<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.--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.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 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 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.