A feed post card: avatar, name and time, two lines of text, a media block and an action bar. The media shows drifting blurred color blobs, like a BlurHash placeholder, while the text breathes.
Also known as: feed post skeleton, social feed placeholder, tweet skeleton loader.
<div class="ldr-skeleton-social-post" role="status" aria-label="Loading content"> <div class="ldr-skeleton-social-post-head"><span></span><span></span><span></span><span></span></div> <span class="ldr-skeleton-social-post-text"></span> <span class="ldr-skeleton-social-post-text"></span> <span class="ldr-skeleton-social-post-media"></span> <div class="ldr-skeleton-social-post-actions"><span></span><span></span><span></span></div> </div>
.ldr-skeleton-social-post {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
display: grid;
gap: calc(var(--s) / 8);
width: calc(var(--s) * 4.8);
padding: calc(var(--s) / 5);
border: 1px solid color-mix(in srgb, var(--ldr-color, #3b82f6) 16%, transparent);
border-radius: calc(var(--s) / 5);
}
.ldr-skeleton-social-post-head {
display: grid;
grid-template-columns: auto 1fr auto;
grid-template-rows: auto auto;
column-gap: calc(var(--s) / 6);
row-gap: calc(var(--s) / 12);
align-items: center;
margin-bottom: calc(var(--s) / 20);
}
.ldr-skeleton-social-post :where(span) {
display: block;
height: calc(var(--s) / 7);
border-radius: var(--s);
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 20%, transparent);
}
.ldr-skeleton-social-post-head span:nth-child(1) {
grid-row: span 2;
width: calc(var(--s) * 0.52);
height: calc(var(--s) * 0.52);
}
.ldr-skeleton-social-post-head span:nth-child(2) { width: 46%; align-self: end; }
.ldr-skeleton-social-post-head span:nth-child(3) {
grid-row: span 2;
width: calc(var(--s) / 2.4);
height: calc(var(--s) / 12);
background: radial-gradient(circle closest-side, color-mix(in srgb, var(--ldr-color, #3b82f6) 34%, transparent) 90%, transparent) 0 0 / 33.4% 100% repeat-x;
}
.ldr-skeleton-social-post-head span:nth-child(4) { width: 26%; height: calc(var(--s) / 9); align-self: start; background: color-mix(in srgb, var(--ldr-color, #3b82f6) 12%, transparent); }
.ldr-skeleton-social-post-text,
.ldr-skeleton-social-post-head span:nth-child(2),
.ldr-skeleton-social-post-head span:nth-child(4) {
animation: ldr-skeleton-social-post-breathe 2s ease-in-out infinite;
}
.ldr-skeleton-social-post-text + .ldr-skeleton-social-post-text { width: 68%; }
.ldr-skeleton-social-post-media {
height: calc(var(--s) * 1.15);
margin: calc(var(--s) / 20) 0;
border-radius: calc(var(--s) / 8);
background:
radial-gradient(38% 60% at 50% 50%, color-mix(in srgb, var(--ldr-color, #3b82f6) 50%, transparent), transparent 70%) 10% 20% / 170% 170% no-repeat,
radial-gradient(34% 55% at 50% 50%, color-mix(in oklch, var(--c) 50%, rgb(236 72 153 / 0.45)), transparent 70%) 90% 80% / 170% 170% no-repeat,
radial-gradient(40% 60% at 50% 50%, color-mix(in srgb, var(--ldr-color, #3b82f6) 30%, transparent), transparent 70%) 60% 0% / 170% 170% no-repeat,
color-mix(in srgb, var(--ldr-color, #3b82f6) 14%, transparent);
animation: ldr-skeleton-social-post-drift 5s ease-in-out infinite;
}
.ldr-skeleton-social-post-actions {
display: flex;
gap: calc(var(--s) / 2.5);
}
.ldr-skeleton-social-post-actions span {
width: calc(var(--s) * 0.6);
height: calc(var(--s) / 5);
background: radial-gradient(circle closest-side, color-mix(in srgb, var(--ldr-color, #3b82f6) 28%, transparent) 92%, transparent) 0 50% / calc(var(--s) / 5) 100% no-repeat, linear-gradient(color-mix(in srgb, var(--ldr-color, #3b82f6) 16%, transparent) 0 0) 100% 50% / 50% 60% no-repeat;
}
@keyframes ldr-skeleton-social-post-breathe {
50% { opacity: 0.5; }
}
@keyframes ldr-skeleton-social-post-drift {
0%, 100% { background-position: 10% 20%, 90% 80%, 60% 0%, 0 0; }
33% { background-position: 80% 40%, 20% 70%, 30% 100%, 0 0; }
66% { background-position: 40% 90%, 60% 10%, 90% 40%, 0 0; }
}
@media (prefers-reduced-motion: reduce) {
.ldr-skeleton-social-post, .ldr-skeleton-social-post *, .ldr-skeleton-social-post::before, .ldr-skeleton-social-post::after, .ldr-skeleton-social-post *::before, .ldr-skeleton-social-post *::after {
animation-duration: 3s;
}
}--ldr-color on the element or a parent.
Currently previewing #3b82f6.--ldr-size. Every dimension in the snippet is
derived from it. Currently previewing 64px.animation shorthand.
Delays on the children are relative, so they keep their rhythm.role="status" and an
aria-label so screen readers announce the wait. The reduced-motion rule
at the end slows the animation for users who asked for less motion.