In this new tutorial, we’ll learn how to create a rotating text animation effect using CSS variables and JavaScript. Although we can certainly build a simple text-changing animation in pure CSS, we’ll put in the loop JavaScript to make things more maintainable and effective.
Basic Text Rotating Animation
We’ll define a h1
element where we’ll put as span
s all the words that we want to animate. By default, all span
s will have the data-bg-color
and data-color
custom attributes that will determine their text and background colors accordingly. Plus, initially, only the span
with the current
class will be visible—the first one in our case.
Here’s the required markup format:
1 |
<h1 class="words-wrapper"> |
2 |
I want to learn <span class="css">CSS</span> and |
3 |
<span class="words"> |
4 |
<span class="current" data-bg-color="#ffc703" data-color="#000">React</span> |
5 |
<span data-bg-color="#004e98" data-color="#fff">TypeScript</span> |
6 |
<span data-bg-color="#8cb369" data-color="#000">Python</span> |
7 |
<span data-bg-color="#104911" data-color="#fff">PrestaShop</span> |
8 |
<span data-bg-color="#b8c0ff" data-color="#000">Ruby</span> |
9 |
<span data-bg-color="#e71d36" data-color="#fff">Angular</span> |
10 |
<span data-bg-color="#e2c044" data-color="#000">WordPress</span> |
11 |
<span data-bg-color="#065a82" data-color="#fff">Node</span> |
12 |
</span>.
|
13 |
</h1>
|
As we’ve done in many other tutorials in the past, we’ll use CSS Grid to place all words on top of each other. But remember that each time, only the word with the current
class will appear.
Here are all the required styles:
1 |
.words-wrapper { |
2 |
font-size: 40px; |
3 |
font-weight: bold; |
4 |
text-align: center; |
5 |
}
|
6 |
|
7 |
.words-wrapper .css { |
8 |
color: #2ec4b6; |
9 |
}
|
10 |
|
11 |
.words-wrapper .words { |
12 |
display: inline-grid; |
13 |
padding: 0 10px; |
14 |
border-radius: 6px; |
15 |
color: var(--color, #000); |
16 |
background: var(--color-bg, #ffc703); |
17 |
}
|
18 |
|
19 |
.words-wrapper .words span { |
20 |
grid-area: 1/1; |
21 |
display: none; |
22 |
}
|
23 |
|
24 |
.words-wrapper .words span.current { |
25 |
display: block; |
26 |
}
|
To show a different word after a certain amount of time, we’ll follow this approach:
- Every 1.5 seconds, we’ll target the visible (active) word.
- Then, we’ll find its adjacent sibling word if it exists, otherwise, we’ll get the first word.
- We’ll remove the
current
class from the active word and add it to the new element. - Finally, we’ll grab the colors of the new active word and update the corresponding CSS variables.
Here’s the required JavaScript:
1 |
const wrapper = document.querySelector(".words"); |
2 |
const CURRENT_CLASS = "current"; |
3 |
|
4 |
setInterval(() => { |
5 |
const currentWord = wrapper.querySelector("span.current"); |
6 |
const nextWord = currentWord.nextElementSibling |
7 |
? currentWord.nextElementSibling |
8 |
: wrapper.firstElementChild; |
9 |
currentWord.classList.remove(CURRENT_CLASS); |
10 |
nextWord.classList.add(CURRENT_CLASS); |
11 |
wrapper.style.setProperty("--color", nextWord.dataset.color); |
12 |
wrapper.style.setProperty("--color-bg", nextWord.dataset.bgColor); |
13 |
}, 1500); |
We’ll end up with the following demo:
Complex Text Rotating Animation
Let’s now build on the previous example and create something more elegant!
The markup will remain the same apart from one change; this time, we’ll add the next
class to the word that comes after the active one, like this:
1 |
<h1 class="words-wrapper"> |
2 |
I want to learn <span class="css">CSS</span> and |
3 |
<span class="words"> |
4 |
<span class="current" data-bg-color="#ffc703" data-color="#000">React</span> |
5 |
<span class="next" data-bg-color="#004e98" data-color="#fff">TypeScript</span> |
6 |
<span data-bg-color="#8cb369" data-color="#000">Python</span> |
7 |
<span data-bg-color="#104911" data-color="#fff">PrestaShop</span> |
8 |
<span data-bg-color="#b8c0ff" data-color="#000">Ruby</span> |
9 |
<span data-bg-color="#e71d36" data-color="#fff">Angular</span> |
10 |
<span data-bg-color="#e2c044" data-color="#000">WordPress</span> |
11 |
<span data-bg-color="#065a82" data-color="#fff">Node</span> |
12 |
</span>.
|
13 |
</h1>
|
Unlike the previous example, we won’t use CSS Grid to position the words, but instead, each word will be an absolute positioned element. Their immediate parent (i.e. the .words
element) will have a width that will depend on the width of the active word. The next word will appear smoothly from bottom to top and then move to the top to disappear.
On small screens where the text splits into more than one line, to avoid line flickering that may occur depending on the active word’s width, we’ll set all words’ widths equal to the width of the largest word. In this case, we’ll use the !important
keyword to override the width of the .words
element that’s set via the width
CSS variable.
Here are all the required styles:
1 |
.words-wrapper { |
2 |
font-size: 40px; |
3 |
font-weight: bold; |
4 |
text-align: center; |
5 |
}
|
6 |
|
7 |
.words-wrapper .css { |
8 |
color: #2ec4b6; |
9 |
}
|
10 |
|
11 |
.words-wrapper .words { |
12 |
display: inline-block; |
13 |
position: relative; |
14 |
vertical-align: bottom; |
15 |
width: var(--width); |
16 |
height: 60px; |
17 |
padding: 0 10px; |
18 |
border-radius: 6px; |
19 |
color: var(--color, #000); |
20 |
background: var(--color-bg, #ffc703); |
21 |
box-sizing: content-box; |
22 |
transition: all 0.7s; |
23 |
}
|
24 |
|
25 |
.words-wrapper .words span { |
26 |
position: absolute; |
27 |
top: 0; |
28 |
left: 50%; |
29 |
opacity: 0; |
30 |
transform: translate(-50%, -100%); |
31 |
transition: transform 0.7s, opacity 0.25s 0.25s; |
32 |
}
|
33 |
|
34 |
.words-wrapper .words span.current { |
35 |
opacity: 1; |
36 |
transform: translate(-50%, 0); |
37 |
}
|
38 |
|
39 |
.words-wrapper .words span.next { |
40 |
transform: translate(-50%, 100%); |
41 |
}
|
42 |
|
43 |
@media (max-width: 700px) { |
44 |
.words-wrapper .words { |
45 |
width: var(--width-mobile) !important; |
46 |
}
|
47 |
}
|
When the DOM is ready, we’ll specify the initial width of the .words
wrapper by updating the values of the width
and width-mobile
CSS variable values.
Next, similar to the previous example, to show a different word after a certain amount of time, we’ll follow this approach:
- Every 1.5 seconds, we’ll target the visible (active) and next active words.
- Then, we’ll find the adjacent sibling word of the next active word if it exists, otherwise, we’ll get the first word.
- We’ll remove the
current
class from the active word and thenext
class from the next active word. - We’ll add the
current
class to the next active word and thenext
class to its adjacent sibling word. - Finally, we’ll grab the width and colors of the new active word and update the corresponding CSS variables.
Here’s the required JavaScript:
1 |
const wrapper = document.querySelector(".words"); |
2 |
const words = wrapper.querySelectorAll("span"); |
3 |
const currentWord = wrapper.querySelector("span.current"); |
4 |
const wordsWidths = Array.from(words).map((word) => word.offsetWidth); |
5 |
const maxWordsWidth = Math.max(...wordsWidths); |
6 |
const CURRENT_CLASS = "current"; |
7 |
const NEXT_CLASS = "next"; |
8 |
|
9 |
wrapper.style.setProperty("--width", `${currentWord.offsetWidth}px`); |
10 |
wrapper.style.setProperty("--width-mobile", `${maxWordsWidth}px`); |
11 |
|
12 |
setInterval(() => { |
13 |
const currentWord = wrapper.querySelector("span.current"); |
14 |
const nextWord = wrapper.querySelector("span.next"); |
15 |
const nextNextWord = nextWord.nextElementSibling |
16 |
? nextWord.nextElementSibling |
17 |
: wrapper.firstElementChild; |
18 |
currentWord.classList.remove(CURRENT_CLASS); |
19 |
nextWord.classList.remove(NEXT_CLASS); |
20 |
nextWord.classList.add(CURRENT_CLASS); |
21 |
nextNextWord.classList.add(NEXT_CLASS); |
22 |
wrapper.style.setProperty("--color", nextWord.dataset.color); |
23 |
wrapper.style.setProperty("--color-bg", nextWord.dataset.bgColor); |
24 |
wrapper.style.setProperty("--width", `${nextWord.offsetWidth}px`); |
25 |
}, 1500); |
I recommend you use the browser console to inspect the words and see how they behave.
We’ll end up with the following demo:
Here’s what we’re going to create. It’s a great addition to portfolio websites or hero sections to highlight things.
Conclusion
In this tutorial, we used CSS variables and JavaScript to build a text animation effect where certain words change after a specific period. This will hopefully inspire you to create even more exciting things by changing perhaps more than one word simultaneously, etc.
As always, thanks a lot for reading!
Discover More JavaScript-Based Text Effects
Interested in more creative text animation effects with JavaScript? If so, check out these tutorials:
Recent Comments