AI CSS Animations

Playlist Row Hover Effect

A track list in the Spotify style: hovering a row tints it, rolls the track number up into a play button and fades in a like button. The number and the icon share one grid cell.

Also known as: spotify hover effect css, track list hover play button, song row hover.

  1. Night DriveLumen
  2. Paper PlanesJuno Park
  3. Low TideThe Harbors

HTML

<ol class="hfx-playlist-row">
  <li>
    <button class="hfx-playlist-row-play" aria-label="Play Night Drive"><span>1</span><svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M7.5 5.2v13.6a.8.8 0 0 0 1.2.7l10.6-6.8a.8.8 0 0 0 0-1.4L8.7 4.5a.8.8 0 0 0-1.2.7z"/></svg></button>
    <span class="hfx-playlist-row-cover" style="--hue: 0deg"></span>
    <span class="hfx-playlist-row-title"><b>Night Drive</b><small>Lumen</small></span>
    <button class="hfx-playlist-row-like" aria-label="Like Night Drive"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.2 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z"/></svg></button>
    <time>3:42</time>
  </li>
  <li>
    <button class="hfx-playlist-row-play" aria-label="Play Paper Planes"><span>2</span><svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M7.5 5.2v13.6a.8.8 0 0 0 1.2.7l10.6-6.8a.8.8 0 0 0 0-1.4L8.7 4.5a.8.8 0 0 0-1.2.7z"/></svg></button>
    <span class="hfx-playlist-row-cover" style="--hue: 150deg"></span>
    <span class="hfx-playlist-row-title"><b>Paper Planes</b><small>Juno Park</small></span>
    <button class="hfx-playlist-row-like" aria-label="Like Paper Planes"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.2 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z"/></svg></button>
    <time>2:58</time>
  </li>
  <li>
    <button class="hfx-playlist-row-play" aria-label="Play Low Tide"><span>3</span><svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M7.5 5.2v13.6a.8.8 0 0 0 1.2.7l10.6-6.8a.8.8 0 0 0 0-1.4L8.7 4.5a.8.8 0 0 0-1.2.7z"/></svg></button>
    <span class="hfx-playlist-row-cover" style="--hue: 270deg"></span>
    <span class="hfx-playlist-row-title"><b>Low Tide</b><small>The Harbors</small></span>
    <button class="hfx-playlist-row-like" aria-label="Like Low Tide"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.2 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z"/></svg></button>
    <time>4:15</time>
  </li>
</ol>

CSS

.hfx-playlist-row {
  --c: var(--hfx-color, #3b82f6);
  font-size: var(--hfx-size, 16px);
  width: 20em;
  margin: 0;
  padding: 0;
  list-style: none;
  font-family: system-ui, sans-serif;
}
.hfx-playlist-row li {
  display: grid;
  grid-template-columns: 1.6em 2.4em 1fr auto 2.4em;
  align-items: center;
  gap: 0.7em;
  padding: 0.4em 0.6em;
  border-radius: 0.6em;
  transition: background-color 0.2s ease;
}
.hfx-playlist-row-play {
  display: grid;
  place-items: center;
  width: 1.6em;
  height: 1.6em;
  padding: 0;
  overflow: hidden;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}
.hfx-playlist-row-play > * {
  grid-area: 1 / 1;
  transition: transform 0.3s cubic-bezier(0.3, 0.7, 0.4, 1), opacity 0.2s ease;
}
.hfx-playlist-row-play span {
  color: color-mix(in srgb, currentColor 55%, transparent);
  font-variant-numeric: tabular-nums;
}
.hfx-playlist-row-play svg {
  width: 1.2em;
  height: 1.2em;
  color: var(--c);
  opacity: 0;
  transform: translateY(120%);
}
.hfx-playlist-row-cover {
  width: 2.4em;
  height: 2.4em;
  border-radius: 0.35em;
  background:
    radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.55), transparent 45%),
    linear-gradient(135deg, color-mix(in srgb, var(--c) 45%, #fff), var(--c) 60%, color-mix(in srgb, var(--c) 70%, #000));
  filter: hue-rotate(var(--hue, 0deg));
}
.hfx-playlist-row-title {
  display: grid;
  min-width: 0;
  line-height: 1.25;
}
.hfx-playlist-row-title b { font-weight: 600; }
.hfx-playlist-row-title small {
  color: color-mix(in srgb, currentColor 60%, transparent);
  font-size: 0.8em;
}
.hfx-playlist-row-like {
  display: grid;
  padding: 0;
  border: 0;
  background: none;
  color: var(--c);
  cursor: pointer;
  opacity: 0;
  transform: scale(0.6);
  transition: opacity 0.2s ease, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.hfx-playlist-row-like svg { width: 1.15em; height: 1.15em; }
.hfx-playlist-row time {
  color: color-mix(in srgb, currentColor 60%, transparent);
  font-size: 0.8em;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.hfx-playlist-row li:is(:hover, :focus-within) { background: color-mix(in srgb, var(--c) 12%, transparent); }
.hfx-playlist-row li:is(:hover, :focus-within) .hfx-playlist-row-play span {
  opacity: 0;
  transform: translateY(-120%);
}
.hfx-playlist-row li:is(:hover, :focus-within) .hfx-playlist-row-play svg {
  opacity: 1;
  transform: none;
}
.hfx-playlist-row li:is(:hover, :focus-within) .hfx-playlist-row-like {
  opacity: 1;
  transform: none;
}

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

Tailwind CSS version

The same effect using Tailwind utility classes. No custom CSS or config needed.

<ol class="w-80">
  <li class="group grid grid-cols-[1.5rem_2.5rem_1fr_auto_2.5rem] items-center gap-3 rounded-lg px-2.5 py-1.5 transition-colors hover:bg-blue-500/10">
    <button aria-label="Play Night Drive" class="grid h-6 w-6 place-items-center overflow-hidden">
      <span class="[grid-area:1/1] text-gray-500 transition duration-300 group-hover:-translate-y-full group-hover:opacity-0">1</span>
      <svg class="[grid-area:1/1] h-5 w-5 translate-y-full text-blue-500 opacity-0 transition duration-300 group-hover:translate-y-0 group-hover:opacity-100" viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M7.5 5.2v13.6a.8.8 0 0 0 1.2.7l10.6-6.8a.8.8 0 0 0 0-1.4L8.7 4.5a.8.8 0 0 0-1.2.7z"/></svg>
    </button>
    <span class="h-10 w-10 rounded-md bg-gradient-to-br from-blue-300 to-blue-600"></span>
    <span class="grid leading-tight"><b class="font-semibold">Night Drive</b><small class="text-gray-500">Lumen</small></span>
    <button aria-label="Like Night Drive" class="scale-75 text-blue-500 opacity-0 transition group-hover:scale-100 group-hover:opacity-100">
      <svg class="h-5 w-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 20s-7.5-4.6-7.5-10A4.3 4.3 0 0 1 12 7.2 4.3 4.3 0 0 1 19.5 10c0 5.4-7.5 10-7.5 10z"/></svg>
    </button>
    <time class="text-right text-sm text-gray-500">3:42</time>
  </li>
</ol>

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

Sliding Pill Nav Hover Effect
Directional Underline Nav Hover Effect
Dot Indicator Nav Hover Effect
Vertical Nav Indicator Hover Effect