Basics: How the text scroll effect works
This text scroll effect can be achieved using simple CSS animation properties. To make text scroll horizontally on a webpage, we’ll add sample text in a container element and apply the transform:translateX()
CSS property to the text as shown below ↓
The text is moved using transform:translateX()
property inside a @keyframes
animation. The animation starts with the text at 0% (initial position) and moves to -100% (text moves out of view to the left).
The scroll animation is applied using:
1 |
animation: scrollText 25s linear infinite; |
where:
-
scrollText
is the name of the animation -
25s
is the duration of the animation -
linear
makes the scrolll speed consistent -
inifinite
means the animation will repeat forever
To scroll text vertically, we use transform:translateY
property inside a @keyframe
animation. In the next example, we have duplicated the text and made the container taller to accommodate the scrolling text.
How to create an infinite horizontal and vertical text scroll effect
Let’s combine all the tricks we have learned so far and create a stylish infinite vertical and horizontal text scroll effect.
HTML structure
In our HTML structure, we have separate containers for each scroll direction. The horizontal scroll will occupy the main content area while the vertical scroll will be in a sidebar. By the end of this tutorial, we will have something like this:
The HTML structure will look like this:
1 |
<div class="main-container"> |
2 |
<div class="scroll-container"> |
3 |
<div class="text-row"> |
4 |
<div class="infinite-scroll"> |
5 |
<div class="text-scroll"> |
6 |
<!-- text added here -->
|
7 |
</div>
|
8 |
|
9 |
<div class="text-scroll"> |
10 |
<!-- text added here -->
|
11 |
</div>
|
12 |
</div>
|
13 |
</div>
|
14 |
</div>
|
15 |
|
16 |
<div class="vertical-scroll-container"> |
17 |
<div class="vertical-infinite-scroll"> |
18 |
<div class="vertical-text-scroll"> |
19 |
<!-- text added here -->
|
20 |
</div>
|
21 |
<div class="vertical-text-scroll"> |
22 |
<!-- text added here -->
|
23 |
</div>
|
24 |
</div>
|
25 |
</div>
|
26 |
</div>
|
The scrolling text will be added dynamically using JavaScript. For each scroll direction, we will duplicate each pair of scrolling text elements to create a seamless looping animation.
Main CSS styles
Firstly, let’s remove the default spacing on the body, so set overflowX: hidden
to prevent horizontal scrolling. We’ll also apply a custom font.
1 |
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@800&display=swap"); |
2 |
body { |
3 |
font-family: "Montserrat", sans-serif; |
4 |
margin: 0; |
5 |
padding: 0; |
6 |
overflow-x: hidden; |
7 |
display: flex; |
8 |
min-height: 100vh; |
9 |
color: #000; |
10 |
background-color: #adea74; |
11 |
}
|
We have also applied a bright Envato green background to provide contrast with the “soft serve” of the scrolling text containers.
Align the sections side by side using flexbox and set the main container to full width.
1 |
.main-container { |
2 |
display: flex; |
3 |
width: 100%; |
4 |
}
|
The horizontal scroll container will take the full width of the webpage, while the vertical scroll container will have a width of 200px.
1 |
.scroll-container { |
2 |
flex-grow: 1; |
3 |
overflow: hidden; |
4 |
}
|
5 |
|
6 |
.vertical-scroll-container { |
7 |
width: 200px; |
8 |
background-color: #fef5ee; |
9 |
border-left: 2px solid rgba(0, 0, 0, 0.1); |
10 |
overflow: hidden; |
11 |
}
|
Add scroll text to the webpage with JavaScript
Before we start animating, let’s inject the text to be animated to the Dom with JavaScript. This could be done manually too, but adding text dynamically like this gives us more flexibility.
Get the horizontal and vertical text scroll elements, in our case, these two elements.
1 |
const horizontalTextScroll = document.querySelectorAll(".text-scroll"); |
2 |
const verticalTextScroll = document.querySelectorAll( |
3 |
".vertical-text-scroll" |
4 |
);
|
Define a list of phrases that will appear in our scroll animation.
1 |
const items = [ |
2 |
"Digital Strategy", |
3 |
"Design System", |
4 |
"Brand Strategy", |
5 |
"Website Design", |
6 |
"SEO Optimization", |
7 |
"Content Strategy", |
8 |
];
|
We will then loop through each phrase in the items array and pass it as an argument to the addScrollItem
function. This function is responsible for adding the text and star to both the horizontal and vertical scrolling containers on the webpage.
1 |
items.forEach((item) => { |
2 |
addItemToScrolls(item); |
3 |
});
|
To create an infinite horizontal scrolling effect, we loop through each element defined by horizontalTextScroll
and since there are two of them, we insert the same set of text items into both.
The duplication is important to ensure that when one set scrolls out of view, the next scrolls in ensuring a seamless loop.
The function look like this:
1 |
function addItemToScrolls(text) { |
2 |
|
3 |
horizontalTextScroll.forEach((scrollItem) => { |
4 |
const textItem = document.createElement("span"); |
5 |
textItem.classList.add("text-item"); |
6 |
textItem.innerHTML = text; |
7 |
scrollItem.appendChild(textItem); |
8 |
|
9 |
});
|
10 |
}
|
In the same loop, we will also add a star (★) between each phrase to enhance the visual experience. Update the code as shown below.
1 |
function addItemToScrolls(text) { |
2 |
|
3 |
horizontalTextScroll.forEach((scrollItem) => { |
4 |
const textItem = document.createElement("span"); |
5 |
textItem.classList.add("text-item"); |
6 |
textItem.innerHTML = text; |
7 |
scrollItem.appendChild(textItem); |
8 |
|
9 |
const star = createStar(); |
10 |
star.classList.add("star"); |
11 |
scrollItem.appendChild(star); |
12 |
});
|
13 |
}
|
14 |
|
15 |
function createStar() { |
16 |
const star = document.createElement("span"); |
17 |
star.textContent = "★"; |
18 |
return star; |
19 |
}
|
To create an infinite vertical scrolling effect, follow the same steps as the horizontal scroll except rather than adding the full phrase, for example, “Digital Stategy”, we only take the first word, in this case, “Digital” and add it to the scroll element.
We will also perform duplication to ensure a continuous loop.
1 |
function addItemToScrolls(text) { |
2 |
|
3 |
horizontalTextScroll.forEach((scrollItem) => { |
4 |
const textItem = document.createElement("span"); |
5 |
textItem.classList.add("text-item"); |
6 |
textItem.innerHTML = text; |
7 |
scrollItem.appendChild(textItem); |
8 |
|
9 |
const star = createStar(); |
10 |
star.classList.add("star"); |
11 |
scrollItem.appendChild(star); |
12 |
});
|
13 |
|
14 |
verticalTextScroll.forEach((scrollItem) => { |
15 |
const textItem = document.createElement("span"); |
16 |
textItem.classList.add("vertical-text-item"); |
17 |
const firstWord = text.split(" ")[0]; |
18 |
textItem.textContent = firstWord; |
19 |
scrollItem.appendChild(textItem); |
20 |
|
21 |
const star = createStar(); |
22 |
scrollItem.appendChild(star); |
23 |
});
|
24 |
}
|
Animating the text with CSS
We’ll start with the horizontal section. Define the animations using CSS @keyframes.
1 |
@keyframes scroll { |
2 |
0% { |
3 |
transform: translateX(0); |
4 |
}
|
5 |
100% { |
6 |
transform: translateX(-50%); |
7 |
}
|
8 |
}
|
The element will have no movement at 0% (its original position), and by the end of the animation, the text will have moved by 50% of its width. This means the text will slide halfway off the left side of its parent container.
Since we are duplicating the content inside the same container, we use translateX(-50%)
so that when the first half moves out of view, the second half takes its place, creating a seamless scrolling loop.
To ensure the animation repeats infinitely and runs at a constant speed, set the animation to 25s linear infinite .
1 |
.infinite-scroll { |
2 |
animation: scroll 25s linear infinite; |
3 |
|
4 |
}
|
Set display:flex
to ensure the duplicated text elements are displayed in a row.
1 |
.infinite-scroll { |
2 |
display: flex; |
3 |
animation: scroll 25s linear infinite; |
4 |
|
5 |
}
|
To draw attention to the content as it moves across the webpage, increase the font size of the text and the star and add a bigger font weight to the text.
1 |
.text-item { |
2 |
font-size: 2rem; |
3 |
font-weight: 800; |
4 |
}
|
5 |
|
6 |
.star { |
7 |
font-size: 2rem; |
8 |
margin: 10px 30px; |
9 |
}
|
Vertical scrolling animation
To create the vertical scrolling effect, we will follow the same animation steps, but this time, instead of moving the content sideways using translateX
, we will use translateY
to move the content vertically. We’re also presented with some interesting challenges with the way the text is laid out—we’ll solve those with Flexbox and another unusual CSS property.
Define another keyframe animation similar to the previous one for the vertical infinite scroll.
1 |
@keyframes scrollVertical { |
2 |
0% { |
3 |
transform: translateY(0); |
4 |
}
|
5 |
100% { |
6 |
transform: translateY(-50%); |
7 |
}
|
8 |
}
|
Add the animation to the vertical scroll container.
1 |
.vertical-infinite-scroll { |
2 |
display: flex; |
3 |
flex-direction: column; |
4 |
animation: scrollVertical 25s linear infinite; |
5 |
}
|
The text on the vertical scroll needs to be flipped to ensure the characters are arranged from top to bottom. In CSS, we use the writing-mode
property which allows us to define the direction in which text is laid out.
1 |
.vertical-text-item { |
2 |
writing-mode: vertical-rl; |
3 |
|
4 |
}
|
After setting the writing mode, we also need to flip the text so it appears upright from top to bottom using the transform: rotate(360)
property.
1 |
.vertical-text-item { |
2 |
writing-mode: vertical-rl; |
3 |
transform: rotate(180deg); |
4 |
|
5 |
}
|
Now we have this effect on the vertical scroll.



Apply flex:display
to ensure the two pairs of text are arranged in a column and set flex-direction: column
to stack them vertically. Additionally, set flex-shrink: 0;
to stop the items from shrinking when there is insufficient space.
1 |
.vertical-text-scroll { |
2 |
display: flex; |
3 |
flex-direction: column; |
4 |
align-items: center; |
5 |
flex-shrink: 0; |
6 |
}
|
Final scrolling text demo
Here is a reminder of the final scroll demo.
If you are looking for more advanced effects, check out our tutorial on How to Build Horizontal Marquee Effects with GSAP, and take your animations to the next level with GSAP.
Conclusion
In this tutorial, we have used simple CSS properties to achieve horizontal and vertical text scroll effects. These effects can easily be integrated into any design, making it an eye-catching addition to any webpage.