AI CSS Animations

Video Card Skeleton

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.

HTML

<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>

CSS

.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;
  }
}

Customizing this loader

  • Color: set --ldr-color on the element or a parent. Currently previewing #3b82f6.
  • Size: set --ldr-size. Every dimension in the snippet is derived from it. Currently previewing 64px.
  • Speed: change the duration on the animation shorthand. Delays on the children are relative, so they keep their rhythm.
  • Accessibility: the root carries 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.

Similar loaders