AI CSS Animations

Video Thumbnail Hover Effect

A video card that starts a preview on hover: the play button fades, clouds drift across the frame and a progress bar fills along the bottom. The playback is faked with transforms, no video file.

Also known as: video hover preview css, youtube thumbnail hover, video card hover effect.

HTML

<a class="hfx-video-thumbnail" href="#">
  <span class="hfx-video-thumbnail-frame">
    <span class="hfx-video-thumbnail-scene"></span>
    <span class="hfx-video-thumbnail-play" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M9 6.5v11l9-5.5z"/></svg></span>
    <span class="hfx-video-thumbnail-time">12:48</span>
    <span class="hfx-video-thumbnail-progress"></span>
  </span>
  <span class="hfx-video-thumbnail-title">Sunset timelapse from the ridge</span>
  <span class="hfx-video-thumbnail-meta">Trail Notes, 18K views</span>
</a>

CSS

.hfx-video-thumbnail {
  --c: var(--hfx-color, #3b82f6);
  display: flex;
  flex-direction: column;
  gap: 0.3em;
  width: 14em;
  font: 400 var(--hfx-size, 16px) system-ui, sans-serif;
  color: inherit;
  text-decoration: none;
}
.hfx-video-thumbnail-frame {
  position: relative;
  isolation: isolate;
  aspect-ratio: 16 / 9;
  margin-bottom: 0.3em;
  overflow: hidden;
  border-radius: 0.7em;
}
.hfx-video-thumbnail-scene {
  position: absolute;
  inset: 0;
  background: radial-gradient(75% 42% at 18% 112%, color-mix(in srgb, var(--c) 20%, #0b0a1f) 99%, transparent),
    radial-gradient(66% 36% at 92% 108%, color-mix(in srgb, var(--c) 26%, #0b0a1f) 99%, transparent),
    conic-gradient(from 128deg at 70% 42%, transparent 0, color-mix(in srgb, var(--c) 32%, #1e1b4b) 0.4deg 103.6deg, transparent 104deg),
    conic-gradient(from 120deg at 92% 55%, transparent 0, color-mix(in srgb, var(--c) 32%, #1e1b4b) 0.4deg 119.6deg, transparent 120deg),
    conic-gradient(from 124deg at 28% 38%, transparent 0, color-mix(in srgb, var(--c) 42%, #6b2159) 0.4deg 111.6deg, transparent 112deg),
    conic-gradient(from 126deg at 50% 52%, transparent 0, color-mix(in srgb, var(--c) 42%, #6b2159) 0.4deg 107.6deg, transparent 108deg),
    radial-gradient(circle at 36% 56%, #fef3c7 0 0.9em, rgb(254 215 170 / 0.55) 1em, transparent 3.8em),
    linear-gradient(color-mix(in srgb, var(--c) 55%, #1e1b4b), color-mix(in srgb, var(--c) 35%, #f472b6) 55%, color-mix(in srgb, var(--c) 12%, #fdba74) 78%);
  transition: transform 0.6s cubic-bezier(0.25, 0.8, 0.25, 1);
}
/* a strip of clouds twice the frame's width that repeats halfway, so its drift loops without a seam */
.hfx-video-thumbnail-scene::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 200%;
  height: 55%;
  background:
    radial-gradient(20% 28% at 22% 45%, rgb(255 255 255 / 0.4), transparent 70%),
    radial-gradient(14% 20% at 36% 28%, rgb(255 255 255 / 0.3), transparent 70%),
    radial-gradient(18% 24% at 78% 60%, rgb(255 255 255 / 0.3), transparent 70%);
  background-size: 50% 100%;
}
.hfx-video-thumbnail-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  width: 2.6em;
  height: 2.6em;
  margin: auto;
  border-radius: 50%;
  background: rgb(0 0 0 / 0.45);
  color: #fff;
  transition: opacity 0.25s ease, transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.hfx-video-thumbnail-play svg {
  width: 1.2em;
  height: 1.2em;
  fill: currentColor;
}
.hfx-video-thumbnail-time {
  position: absolute;
  right: 0.55em;
  bottom: 0.55em;
  padding: 0.15em 0.4em;
  border-radius: 0.3em;
  background: rgb(0 0 0 / 0.7);
  color: #fff;
  font-size: 0.7em;
  font-weight: 600;
  transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
}
.hfx-video-thumbnail-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 0.22em;
  background: rgb(255 255 255 / 0.35);
  opacity: 0;
  transition: opacity 0.2s ease;
}
.hfx-video-thumbnail-progress::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--c);
  transform: scaleX(0);
  transform-origin: 0 50%;
}
.hfx-video-thumbnail-title {
  font-size: 0.95em;
  font-weight: 600;
  line-height: 1.3;
  transition: color 0.2s ease;
}
.hfx-video-thumbnail-meta {
  font-size: 0.78em;
  opacity: 0.65;
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-scene {
  transform: scale(1.04);
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-scene::after {
  animation: hfx-video-thumbnail-drift 10s linear infinite;
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-play {
  opacity: 0;
  transform: scale(0.6);
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-time {
  transform: translateY(-0.35em);
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-progress {
  opacity: 1;
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-progress::after {
  animation: hfx-video-thumbnail-play 12s linear forwards;
}
.hfx-video-thumbnail:is(:hover, :focus-visible) .hfx-video-thumbnail-title {
  color: var(--c);
}
@keyframes hfx-video-thumbnail-drift {
  to { transform: translateX(-50%); }
}
@keyframes hfx-video-thumbnail-play {
  to { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .hfx-video-thumbnail, .hfx-video-thumbnail *, .hfx-video-thumbnail::before, .hfx-video-thumbnail::after, .hfx-video-thumbnail *::before, .hfx-video-thumbnail *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
}

Customizing this snippet

  • Color: set --hfx-color on the element or a parent. Currently previewing #3b82f6.
  • Base size: set --hfx-size. Every other dimension is in em, so the whole thing scales together. Currently previewing 16px.
  • Speed: change the duration on the transition or animation shorthand. Staggered delays are relative, so they keep their rhythm.
  • Reduced motion: the rule at the end of the CSS turns the motion off for people who asked their OS for less of it. Keep it.

Similar hover effects