Article Skeleton
Ten bones, 290 px: 130 px hero, two-line title (85/60 %), body lines tapering 100→70 %, 32 px avatar with name and date — 1.8 s CSS shimmer, no JavaScript.
You'll get access to the interactive demo with a free account.
This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Loaders has 29 effects, including 4 free. Explore the category →
3 usage examples



How it works
The .article-skel card (290 px wide, padding: 16px, border-radius: 16px, background rgba(255,255,255,.02), border rgba(255,255,255,.05)) holds ten bones in a column. The hero image .article-skel-hero spans the full content width (256 × 130 px, border-radius: 10px, margin-bottom: 16px), followed by two title lines: .article-skel-title at 85 % (218 px, 16 px tall, margin-bottom: 8px) and .article-skel-title2 at 60 % (154 px, margin-bottom: 16px). Four .article-skel-p elements of 9 px height follow, with widths set by their position in the parent via :nth-child: child 4 at 100 % (256 px), 5 at 95 % (243 px), 6 at 88 % (225 px), 7 at 70 % (179 px) — an inverted pyramid that visually reproduces the tapering of a prose paragraph. The last gets margin-bottom: 16px; the other three get 8px. Measured total card height: 359 px.
The animation runs on the ::after pseudo-element shared by .skel-bone and .skel-bone-circle. It is positioned absolute with inset: 0 and carries a linear-gradient(90deg, transparent 0, rgba(255,255,255,.1) 50%, transparent 100%) sized at 200% 100%. The shimmerSlide animation moves background-position from −200% 0 to 200% 0, a total travel of 400 %. On a 256 px bone, the highlight sweeps through the element in roughly 25 % of the total duration — about 0.45 s of visible sweep, then 1.35 s of plain surface — over a cycle of 1.8 s (ease-in-out, 0s delay, infinite). The peak is rgba(255,255,255,.1) on a rgba(255,255,255,.08) base: an intentionally subtle highlight. All bones share the same parameters and shimmer in phase.
At the bottom of the card, .article-skel-author (flex, gap: 10px, padding-top: 14px, border-top: 1px solid rgba(255,255,255,.05)) aligns a .skel-bone-circle.article-skel-author-avatar circle of 32 × 32 px with two stacked lines on the right: name (80 × 10 px) and date (60 × 8 px, margin-top: 6px). The width hierarchy (80 then 60 px) mimics a first-name/last-name line followed by a short date. Integration note: the four body-line widths rely on :nth-child(5/6/7) of .article-skel — if any element is inserted before those lines, the positions shift and the widths no longer match the right bones. The .demo-preview wrapper present in the sold code (background #0a0a0f, centered flex, min-height: 300px) is the site's demo container; the effect starts at .article-skel.
Accessibility
- prefers-reduced-motion not handled: no
@media (prefers-reduced-motion: reduce)rule in the sold CSS — measured under Playwright emulation,animationPlayStateremainsrunningregardless of the preference. Add@media (prefers-reduced-motion: reduce) { .skel-bone::after, .skel-bone-circle::after { animation: none; } }to freeze the shimmer for affected users. - No accessibility attributes: no
role="status", noaria-busy, noaria-live. Screen readers see ten emptydivs without context. For a loading state: setaria-busy="true"on the parent container during the fetch,aria-live="polite"to announce incoming content, and remove or replace.article-skelonce data is available. - Ghost nodes: the ten bones are empty elements with no text or label. Set
aria-hidden="true"on.article-skelto remove them from the accessibility tree and avoid ten keyboard-navigation stops. - Deliberately low contrast: the bone base (
rgba(255,255,255,.08)on#0a0a0f) gives roughly 1.5:1 — intentionally invisible. No information is encoded in the color; the skeleton is structural and decorative. WCAG 1.4.11 (non-text UI components) does not apply to a loading placeholder. On a light background, switch the base torgba(0,0,0,.08).
Browser compatibility
The inset: 0 shorthand on the ::after pseudo-element sets the technical floor: Chrome 87, Firefox 87, Safari 14.1, Edge 87 (2020). Everything else — @keyframes, background-size, linear-gradient, border-radius, display: flex — has been available since 2013. Zero dependencies, zero JavaScript.
Without inset (pre-2020), the ::after collapses to 0 × 0 px and the shimmer disappears. The bones remain visible as rgba(255,255,255,.08) blocks with no animation — a recognisable, static loading placeholder.
The code
HTML structure to paste into your page (CSS + JS available with a premium account):
<div class="article-skel">
<div class="skel-bone article-skel-hero"></div>
<div class="skel-bone article-skel-title"></div>
<div class="skel-bone article-skel-title2"></div>
<div class="skel-bone article-skel-p"></div>
<!-- … 4 total -->
<div class="article-skel-author">
<div class="skel-bone-circle article-skel-author-avatar"></div>
<div>
<div class="skel-bone article-skel-author-name"></div>
<div class="skel-bone article-skel-author-date"></div>
</div>
</div>
</div>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
Card width (CSS — .article-skel { width: 290px }) |
290 px | Bones expressed in % (article-skel-hero, title lines, body lines) adapt automatically. For an editorial column: width: 100%; max-width: 680px with an explicit hero height. |
Hero image height (CSS — .article-skel-hero { height: 130px }) |
130 px | The visually dominant surface. 80px for a compact list, 200px for a magazine hero, 0 (or display: none) for an article without an image. |
Shimmer duration (CSS — animation: 1.8s ease-in-out 0s infinite shimmerSlide) |
1.8 s | Applies to all bones simultaneously (0 s delay). 1.2s for a faster perceived load; 2.5s for a calmer feel. To stagger bones: .article-skel-title::after { animation-delay: .15s; } (target ::after, not the bone itself). |
Shimmer intensity (CSS — rgba(255,255,255,.1) in the ::after gradient) |
.1 | Peak opacity of the highlight. .15 to .2 for a more visible effect on a dark background; .05 to make it nearly invisible. The bone base is at .08 — keep the peak below .3 to stay within skeleton conventions. |
Bone radius (CSS — .skel-bone { border-radius: 6px }) |
6 px | 3px for a strict design; 12px for a rounded mobile app. The hero has its own radius (.article-skel-hero { border-radius: 10px }) and should be adjusted separately. |
Title line widths (CSS — .article-skel-title { width: 85% } and .article-skel-title2 { width: 60% }) |
85 % and 60 % | Simulate a two-level heading or a long title followed by a subtitle. Change to 90/75 for a longer title; remove .article-skel-title2 for a single-line title. |
Body line height (CSS — .article-skel-p { height: 9px }) |
9 px | Roughly half the line height of 16 px body text. 11px for denser content; 7px for small secondary text. |
FAQ
.article-skel-p:nth-child(5/6/7) target children 5, 6 and 7 of .article-skel — not the 2nd, 3rd and 4th paragraph elements. If any element is inserted before the body lines in the card (for example an extra subtitle), the positions shift and the widths land on the wrong bones. For a modified structure, replace the :nth-child selectors with distinct classes (.article-skel-p--95, etc.) or inline styles..demo-preview (background #0a0a0f, centered flex, min-height: 300px) is the Effect.Labs site's demonstration frame — it is not required for the skeleton to work. The first two CSS blocks in the sold code (.demo-preview, .demo-preview *… and .demo-preview {…}) can be removed at integration. The effect starts at .article-skel and its associated classes.