A 16:9 thumbnail with a play glyph and duration badge, then avatar, two title lines and channel name. A diagonal highlight slides across every bone using transform only, so it stays cheap.
Also known as: youtube skeleton loader, video thumbnail placeholder, video grid skeleton.
<div class="ldr-skeleton-video" role="status" aria-label="Loading content">
<div class="ldr-skeleton-video-thumb"><span class="ldr-skeleton-video-time"></span></div>
<div class="ldr-skeleton-video-meta">
<span class="ldr-skeleton-video-avatar"></span>
<span class="ldr-skeleton-video-lines"><span></span><span></span><span></span></span>
</div>
</div>.ldr-skeleton-video {
--s: var(--ldr-size, 48px);
--c: var(--ldr-color, #3b82f6);
display: grid;
gap: calc(var(--s) / 5);
width: calc(var(--s) * 4.1);
}
.ldr-skeleton-video-thumb,
.ldr-skeleton-video-avatar,
.ldr-skeleton-video-lines span {
position: relative;
overflow: hidden;
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 18%, transparent);
}
.ldr-skeleton-video-thumb::after,
.ldr-skeleton-video-avatar::after,
.ldr-skeleton-video-lines span::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(110deg, transparent 25%, color-mix(in srgb, var(--ldr-color, #3b82f6) 26%, transparent) 50%, transparent 75%);
transform: translateX(-100%);
animation: ldr-skeleton-video-sheen 1.6s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
.ldr-skeleton-video-meta ::after { animation-delay: 0.12s; }
.ldr-skeleton-video-thumb {
display: grid;
place-items: center;
aspect-ratio: 16 / 9;
border-radius: calc(var(--s) / 6);
}
.ldr-skeleton-video-thumb::before {
content: "";
width: calc(var(--s) * 0.42);
height: calc(var(--s) * 0.48);
margin-left: calc(var(--s) / 12);
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 34%, transparent);
clip-path: polygon(0 0, 100% 50%, 0 100%);
}
.ldr-skeleton-video-time {
position: absolute;
right: calc(var(--s) / 8);
bottom: calc(var(--s) / 8);
width: calc(var(--s) * 0.55);
height: calc(var(--s) / 4.5);
border-radius: calc(var(--s) / 16);
background: color-mix(in srgb, var(--ldr-color, #3b82f6) 34%, transparent);
}
.ldr-skeleton-video-meta {
display: flex;
gap: calc(var(--s) / 5);
}
.ldr-skeleton-video-avatar {
flex: none;
width: calc(var(--s) * 0.6);
height: calc(var(--s) * 0.6);
border-radius: 50%;
}
.ldr-skeleton-video-lines {
flex: 1;
display: grid;
gap: calc(var(--s) / 9);
align-content: start;
padding-top: calc(var(--s) / 24);
}
.ldr-skeleton-video-lines span {
height: calc(var(--s) / 6);
border-radius: var(--s);
}
.ldr-skeleton-video-lines span:nth-child(2) { width: 68%; }
.ldr-skeleton-video-lines span:nth-child(3) { width: 42%; height: calc(var(--s) / 8); margin-top: calc(var(--s) / 20); background: color-mix(in srgb, var(--ldr-color, #3b82f6) 12%, transparent); }
@keyframes ldr-skeleton-video-sheen {
60%, 100% { transform: translateX(100%); }
}
@media (prefers-reduced-motion: reduce) {
.ldr-skeleton-video, .ldr-skeleton-video *, .ldr-skeleton-video::before, .ldr-skeleton-video::after, .ldr-skeleton-video *::before, .ldr-skeleton-video *::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.