Styles
ul {
display: flex;
flex-direction: column;
align-items: start;
gap: var(--space-md);
}
li {
font-size: var(--font-size-lg);
text-transform: uppercase;
padding-inline: var(--space-md);
position: relative;
}
li::before {
content: '';
position: absolute;
inset: 0;
background-color: var(--color-accent);
transform: skew(-15deg);
z-index: -1;
opacity: 0;
transition: opacity 0.5s ease;
}
li:hover::before {
opacity: 1;
}
li:hover {
color: var(--bg-brand);
}
Script
const words = document.querySelectorAll('.scramble li')
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const offset = 3
const delay = 40
words.forEach((word) => {
word.dataset.original = word.textContent
word.addEventListener('mouseenter', () => {
let iteration = 0
const interval = setInterval(() => {
word.textContent = scrambleWord(word, iteration)
if (iteration === word.textContent.length + offset) clearInterval(interval)
iteration += 1 / 2
}, delay)
})
})
function scrambleWord(word, iteration) {
return word.textContent
.split('')
.map((letter, index) =>
iteration < index + offset
? letters[Math.floor(Math.random() * letters.length)]
: word.dataset.original[index]
)
.join('')
}