Typewriter Effect with Alternating Text in Pure CSS3

Valentyn Vasylenko
4 min readNov 5, 2018

--

Check out this amazing animation live — https://hackeryou.com/

If you are looking for ways to make your static page feel more “alive” with pure CSS3 — you are in the right place! Typing animation, better known as a “Typewriter” effect, is one of those cheap tricks that are very easy to implement but never fail to impress.

The effect that you see on HackerYou’s website can be easily accomplished with JavaScript, but let’s see if we can replicate it using just CSS and a cup of coffee.

Step 1 — Animation without alternating text

Let’s start with the basics and create our HTML first. We will need a H1 tag and a span with our text inside, let’s give it a class name of text_1.

<h1><span class="text_1">I'm a dynamic heading</span></h1>

Now we’re going to give our span some setup styles that will allow our animation to work properly. First of all, since all spans are inline elements by default, we’ll need to set it’s display property to inline-block in order for it to accept width. Also we have to make sure our text doesn’t jump to the next line and hides overflow when being resized.

.text_1 {
overflow: hidden;
white-space: nowrap;
display: inline-block;
position: relative;
}

You know what would go great with our boring text? A caret! Let’s make one using an after pseudo-element and make sure it’s stuck next to our text at all times.

.text_1::after {
content: "|";
position: absolute;
right: 0;
}

It’s time to create some animations. We want our caret to blink once every second and our text to resize from 0 to its original width and back. Here comes the first problem. If we set our text to width: 100% it will take the full width of the page. Our goal is to make sure it never goes beyond its original width, otherwise it will continue animating empty space that comes after your letters.

That’s why instead of percentages we’re going to be using ems. How many ems will you need for your text? Hard to say, you’ll have to adjust it by hand. My rule of thumb is to take the number of letters and spaces in your sentence and divide it by 2. You will still have to play with the result a little bit, but it’s going to be very close.

@keyframes typewriter {
0%, 100% {
width: 0;
}
20%, 80% {
width: 10.2em;
}
}
@keyframes caret {
0%, 100% {
opacity: 0;
}
50% {
opacity: 1;
}
}

Almost done, now let’s add them to our elements. We’re going to be using steps() as our animation-timing-function to replicate the jittery transition from letter to letter as they are being typed. Plus we’re going to set our caret’s number of steps to 1 to completely eliminate any intermediate animations between opacity 1 and 0.

.text_1 {
overflow: hidden;
white-space: nowrap;
animation: typewriter;
display: inline-block;
position: relative;
animation-duration: 10s;
animation-timing-function: steps(25, end);
animation-iteration-count: infinite;
}
.text_1::after {
content: "|";
position: absolute;
right: 0;
animation: caret infinite;
animation-duration: 1s;
animation-timing-function: steps(1, end);
}

Result:

Step 2 — Animation with alternating text

It’s time to supercharge our typewriter. Let’s add one more span inside our H1 and give it a class of text_2.

<h1>
<span class="text_1">Fancy heading on your static web page</span><span class="text_2">What?? How does it change without javascript?</span>
</h1>

Since we agreed not to use JavaScript for this, we’ll have to figure out a way to alternate between our spans with animations only. The approach we’re going to take in this tutorial will be to make keyframes that run at the same time, but are a mirrored reflection of each other.

It means that while “stuff” is going to be happening in one keyframe, the other one should not change and vice-versa.

@keyframes text2 {
0%, 50%, 100% {
width: 0;
}

60%, 90% {
width: 21.2em;
}
}
@keyframes text1 {
0%, 50%, 100% {
width: 0;
}
10%, 40% {
width: 17em;
}
}

Do not forget to change your em values according to the number of letters in your sentences!

Lastly, we’ll apply this animation on our spans and see the final result.

.text_1 {
animation: text1;
}
.text_2 {
animation: text2;
}
.text_1, .text_2 {
overflow: hidden;
white-space: nowrap;
display: inline-block;
position: relative;
animation-duration: 20s;
animation-timing-function: steps(25, end);
animation-iteration-count: infinite;
}
.text_1::after, .text_2::after {
content: "|";
position: absolute;
right: 0;
animation: caret infinite;
animation-duration: 1s;
animation-timing-function: steps(1, end);
}

Final Result

--

--