You’ve probably seen one of these text animations online, especially on portfolio sites or product pages. They offer a neat way of listing several features, services, or other attributes. you’re able to reproduce this effect without animated GIFs or even any JavaScript.

How the Typewriter Animation Works

Here’s the effect I’ll be covering:

I discovered a CSS approach for this animation onDashiell Wood’swebsite, although I’d seen the same effect plenty of times before.

When you’re creating an animation in CSS, it’s important to plan it out in advance, to identify timings. The exact behavior of this animation is:

Animated text that appears to be typing and deleting words to replace “technology” in the phrase “I’m Bobby Jack, a technology writer."

Separately, a second animation creates a blinking cursor effect. This animation is much more straightforward, so I’ll continue by showing how to create it.

Creating an Animated Cursor

If you’re unfamiliar with animations in CSS, this is a great way to start. The cursor (or “caret”) animation is simple, but it demonstrates the core concepts of animation in the browser.

The basic CSS for the cursor looks like this:

Since the cursor should appear to the right of the text placeholder, this CSS uses the::afterpseudo-element andborder-rightto draw a thin vertical line. Thecontentproperty is required for the ::after pseudo-element to appear.

In CSS, animation consists of two parts:

To define frames in CSS, start by using this syntax, which creates a named animation:

Inside the braces, you’ll need to define each distinct part of the animation by its relative time and properties:

Bold text reading “I’m Bobby Jack, a writer.” with a flashing cursor—a thin vertical line—before the word “writer."

You cannot animate every CSS property, so you’ll need to research which ones work well for each requirement. A blinking cursor should alternate between two states: fully showing and fully off. Theopacityproperty is perfect for this kind of alternating display: it can be animated, and it’s easy to use:

These keyframes describe an animation that begins at full opacity (visible). It then progresses to zero opacity (invisible) at the halfway point, before returning to full opacity at the end of the animation. Since the start and end states are the same, you can group them:

A CSS animation displaying the word “print” one letter at a time.

Any element that uses these keyframes will now alternate between visible and invisible, according to the animation settings. Theanimationproperty is a shorthand for many separate properties:

For a blinking cursor, we’ll make use of just four of these: name, duration, timing-function, and iteration-count:

A CSS animation showing a word appearing one letter at a time, then disappearing as if being deleted.

First, we link the animation to the keyframes defined earlier, using theblinkingEffectname. The duration is set to0.5s, so the cursor will blink twice per second. Thelineartiming-function means that the browser will gradually change the opacity value at a constant rate. For example, at the 25% point, opacity should be exactly 0.5. This creates a pleasant fade in/out effect as the opacity changes. Finally, theinfiniteiteration count means that the animation will continue indefinitely.

Animating Text With CSS

Now that you’ve used the basics of CSS animation to create a simple blinking cursor, it’s time to move on to the typewriter effect. I’ll show how to build this in stages, starting with basic markup which looks like this:

The base CSS for this headline uses common properties to style the text:

As with the blinking cursor, we’ll use the CSS propertyanimationto apply keyframes to an element inside the headline; this time, before the placeholder:

So now you’ll just need to define appropriate keyframes for the animation. Starting with a single word, you can create a simple typing animation by adding each letter to the element’s content at each step. The step sizes should be 100 divided by the number of steps, one for each letter.

To show the word “print”, divide 100 by the number of letters, 5, to obtain 20. Now use this as the increment for each keyframe:

There’s still a bit to do, but you can see the animation starting to take shape already:

Next, try printing the word, then deleting it. Since deleting simply reverses the original steps, you can make use of the same grouping syntax from earlier, to use the same property at several points throughout the animation.

The only tricky bit is getting the number of steps—and, therefore, the increments—correct. To keep everything equal, you’ll need twice the number of steps as before, minus one, since the final step shouldn’t occur twice (we’ll look into making that a proper pause soon).

For “print,” this means we have 100 / 11 = 9.0909 steps. For the sake of simplicity, I’ll round that down to 9, giving:

If you try these keyframes, you’ll see a smooth effect that types the word, then deletes it before stopping:

Right now, the pause on the full word (45%–54%) is the same length as the typing of a character. To extend the pause, just tweak the keyframe values. For example, instead of a 9% pause, try one with 20%. This leaves 80% for the rest of the animation, which is now 10 steps, so 8% each:

At this point, for maximum flexibility, it’s a good idea to consider the formula you’re using to generate these keyframes. If each keyframe lasts x% time, then the original five-letter word, “print,” lasted 20%:

With deletion, and a pause (y), you’ll get this variation:

If the pause is twice as long, theny = 2xwhich you can replace in the above formula to give:

This gives a value for x of 8.3 recurring, which I rounded down to 8, then compensated for by rounding y the other way, from 16 to 20. This isn’t very precise, but it’s fine for the purposes of a simple animation. If you want to use values with more decimal places for the keyframe timings, you’re free to do so, it’ll just involve a bit more work if you’re calculating things manually.

Recall that the full animation involves several words, each of which deletes itself until the last. For this, you’ll need a formula to work out the number of “types;” characters typed or deleted:

So, with “print” (5 * 2 + 1) and “digital” (7), that would be 11 + 7 = 18. With a single pause between those two words, the earlier formula becomes:

For more words, the number of pauses is one fewer than the total number of words, so to animate the words “technology,” “print,” “digital,” and “technology,” you’ll need:

With a 7x pause, this gives:

You now have the building blocks to create this specific type of animation with any set of words you choose. Just apply the formula, work out the timings, and write the CSS.

Why Is This Better Than JavaScript?

There’s no equivalent of CSS animation that’s built into JavaScript. You can either write your own animation code—which could be quite a big task—or use a third-party library likeAnime.js. But, even then, you’ll need to learn how to use that library.

Reducing dependencies is usually a good thing. Especially when some users may have JavaScript disabled, it’s best not to rely on it, unless you have to. Practicing progressive enhancement means that everyone can access your core functionality, while bells and whistles are available for those who can experience them.

JavaScript can be unavailable for many reasons: firewalls, network errors, browser configuration, scripting errors. CSS can also be unavailable, but it’s generally less likely to be.

The main drawback with this technique is the work involved in setting it up, especially with longer words or greater numbers of them. Still the sums involved aren’t too complex, and they would be straightforward to automatically generate.

Another issue is that a simple tweak, like adding or removing a letter to fix a typo, will require you to recalculate the full set of keyframes. Again, generating the keyframes from a set of input words would fix this issue.