Vertically scrolling keywords with just a sprinkle of HTML, CSS and Javascript 🪄

I wanted rotating words that iterated through a list for my personal website. After some quick google searching, I couldn't find a solution, so I decided to do it myself.

Alec Di Vito
Alec Di Vito 3 min read

When visiting this website you're greeted with a title that has the last word rotating through a couple of different words. You are probably thinking that some npm library out that created this feature, but you maybe surprised that it's not the case. Instead, you'll be amazed at how little javascript and HTML you need to create this feature. Before continuing, let's examine the end result.

👨‍💼
I updated my website to a more modern design, that's why the video looks different
0:00
/0:15

Words rotating in and out vertically. wow, very cool!

I've created a Codepen you can use and play with as well to see it in action in just a simple example

Rotating text in the browser, WWOOWW

HTML elements

The feature is made up of 2 HTML elements that slowly rotate in and out of visibility. First start with a parent div with 2 children.

<div id='word-rotation'>
    <div>A word</div>
    <div></div>
</div>

At any time, only one of these div blocks will be presented to the user. When a rotation happens, we want to gracefully transition the div up and out of the screen by using transforms.

Javascript

This action requests Javascript and CSS to work together to create a nice animation. Let's start by reviewing the Javascript that I'm using.

// Create a counter that only increments up. Our words. And get the word rotation element
let index = 0;
const words = ["Javascript", "Python", "Rust", "GoLang", "Yaml"]
const rotationItem = document.getElementById('word-rotation')

// This example expects 2 children, make sure they exist
if (heroRotationItem.children[0] && heroRotationItem.children[1]) {
    
    // If they do, loop and change text every 3 seconds
    setInterval(() => {
        const toHide = heroRotationItem.children[index % 2]
        const toShow = heroRotationItem.children[(index + 1) % 2]
        index += 1;
        const nextItem = words[index % words.length]

        toShow.textContent = nextItem;

        toHide.classList.add('fadeOutUp')
        toShow.classList.remove('fadeOutUp')

        toHide.classList.remove('fadeInUp')
        toShow.classList.add('fadeInUp')
    }, 3000)
}

Everything until the setInterval code is pretty straight forward. Create some counters, set some words to use and get the parent that has at least 2 child nodes. However, the core loop is a bit confusing but we can step through it.

We only want to be showing one element at a time, so of the 2 children, we set one toShow and the other toHide. Because we increment the counter every iteration of the loop, we can use the module (%) operator to loop through them. We can also use module so that we always select an existing word from our list.

Once we have the child we want to show, we can set the text content of the node and add our css classes to it. The toHide element can get the fadeOutUp class while the toShow element will get the fadeInUp.

CSS classes

Let's take a closer look at these CSS classes

💡
Consider putting a width on the #word-rotation class
#word-rotation {
    display: inline-grid;
    position: relative;
}

#word-rotation > * {
    grid-column: 1;
    grid-row: 1;
    text-align: left;
    min-width: 250px;
    animation-duration: 500ms;
    animation-fill-mode: both;
    -webkit-animation-duration: 500ms;
    -webkit-animation-fill-mode: both;
    transition: visibility 0s 500ms, opacity 500ms linear;
}

@keyframes fadeInUp {
    from {
        transform: translate3d(0,40px,0)
    }
    to {
        transform: translate3d(0,0,0);
        opacity: 1
    }
}

@keyframes fadeOutUp {
    from {
        transform: translate3d(0,0,0)
    }
    to {
        transform: translate3d(0,-40px,0);
        opacity: 0;
        width: 0px;
    }
}

.fadeInUp {
    opacity: 0;
    animation-name: fadeInUp;
}

.fadeOutUp {
    opacity: 1;
    animation-name: fadeOutUp;
}

I think that everything here is pretty straight forward, except for 1 thing. How do we get the 2 elements to appear on top of each other? Well we can use display: inline-grid for that and assign all children to the same cell using grid-column: 1; grid-row: 1.

We then tie the fadeInUp and fadeOutUp classes to key frames that transition them between 0 opacity and 1 opacity. While this is going on, we translate them in 3D space to new locations on the screen.

Conclusion

This demo showed how to create rotating words in the browser. I showed that it can be easily achieved with just a bit of HTML, CSS and Javascript. Someone else can extend this by creating an NPM library and possibly polishing some of the rough edges that this implementation has (such as issues with screen resizing that I'll leave up to the reader to discover).