Lyric highlighter

by rhelminen

HTML

<div class="lyrics">
  <p class="lyric-line">Lyric line 1</p>
  <p class="lyric-line">Longer lyric line with more text</p>
  <p class="lyric-line">OK!</p>
  <p class="lyric-line">Baby, baby, yeah</p>
  <p class="lyric-line">These are some pretty great lyrics, don't you think?</p>
</div>

SCSS

body {
  background-color: #2d2d2d;
}

.lyrics {
  margin: auto;
  text-align: center;
  display: flex;
  place-items: center;
  flex-direction: column;
}

.lyric-line {
  --lyric-progress: 0%;
  
  /* background-clip: text with a transparent text shows the background "under" the text instead of the text itself */
  background-clip: text;
  color: transparent;
  
  /* 
    This is the color for the line.
    Setting the endpoint of green to the starting point of white gives you a gradient with a hard break
  */
  background-image: linear-gradient(90deg, mediumseagreen 0 var(--lyric-progress), white var(--lyric-progress) 100%)
}

JavaScript

document.addEventListener('DOMContentLoaded', async () => {
	const lyricLines = document.querySelectorAll('.lyric-line')
  
  // Loop through lyric lines and wait for the previous one to finish before going to the next one
  for (let line of lyricLines) {
    await colorLine(line, 1000)
  }
  
  // Color a single line (msTarget is the target length in milliseconds, adjust according to each line of the lyrics)
  function colorLine(line, msTarget) {
  	let lineProgressPercentage = 0
    
    // Return a Promise that, when resolved, will trigger the process for the next line
    return new Promise(resolve => {
      const colorizer = setInterval(() => {
      	// Modify the CSS variable to set the percentage value for the colored portion of the lyric
        line.style.setProperty('--lyric-progress', `${lineProgressPercentage}%`)
        lineProgressPercentage++

				// When progress > 100%, resolve the Promise and clear the interval
        if (lineProgressPercentage > 100) {
        	resolve(line)
          clearInterval(colorizer)
        }
      }, msTarget / 100)
    })
  }
})