There's a little trick we can do to reveal text in a way that looks like it is being typed across the screen.

Figure 1: The typewriter animation running at various stages

We'll use the following HTML to get started:

<div class="typewriter">
  <h1>The cat and the hat.</h1>
</div>>

That's everything. We could use any level of heading or block text but, for this example, we've made the text a Heading 1 and wrapped that up in a .typewriter container to illustrate what we're doing.

Style the Text

First off, we'll make sure the font is monospace. Not only does this give it an authentic typewriter flair, but it ensures that each character has an equal width. That makes the transition between each letter in the animation feel like a letter is being physically typed into the page.

We're also going to increase the letter-spacing a smidgen solely for the purpose of this example, but it can and should be tweaked to get the best feel for the exact font-family and font-size being used.

.typewriter h1 {
  font-family: monospace;
  letter-spacing: .15em;
}

Next, we'll declare a few more properties:

.typewriter h1 {
  margin: 0 auto;
  overflow: auto;
  white-space: no-wrap;
}

As a final touch, let's add a border. This will be the blinking cursor at the end of the text. We'll make a nice little animation specifically for it so it appears to blink as the content is revealed. That's a subtle but sweet refinement that adds to the illusion that the content is being typed.

.typewriter h1 {
  border-right: .15em solid orange;
}

Create the Animation

Let's level this thing up by defining the keyframes for the animation. We'll have two sets, one for the typing and another for the cursor.

The typing seems like it would something super complex but it's merely animating the width of the text from 0 to 100%:

@keyframes typing {
  from { width: 0 }
  to { width: 100% }
}

In other words, the width will be 0 when the animation begins and full width when the animation is complete.

The cursor actually has a little more complexity to it, but it basically still boils down to animating the border-color we set on the text.

@keyframes blinking-cursor {
  from, to { border-color: transparent }
  50% { border-color: orange }
}

The border-color starts out transparent when the animation begins, but changes to orange halfway through and then back to transparent when the animation ends. That creates a blinking effect where the color toggles between being visible and invisible as the animation occurs.

Apply the Animation

The last thing on our list is to hook the two animations we created to our text. Let's add the typing animation first:

.typewriter h1 {
  animation: typing 3.5s steps(40, end);
}

We called the animiation property on our element and told it which set of keyframes to use. Then we instructed the animation to take 3.5 seconds total over 40 frames and then to come to an end. The length of time and number of steps is totally arbitrary to the amount of content that is being animated and what you think makes for a good effect. We call this these sorts of values "Magic Numbers" because they only work in the context in which they are being used as opposed to everything across the board.

That leaves our cursor animation as the final piece. We'll set it to run for a half second and to run infinitely with no end:

.typewriter h1 {
  animation: blinking-cursor .5s step-end infinite;
}

The animation property actually lets us chain the two animations together by separating them with a comma:

.typewriter h1 {
  animation: 
    typing 3.5s steps(40, end),
    blinking-cursor .5s step-end infinite;
}

Putting it All Together

Let's put everything we covered in this chapter together.

<div class="typewriter">
  <h1>The cat and the hat.</h1>
</div>>
.typewriter h1 {
  font-family: monospace; /* Web-safe typewriter-like font */
  overflow: hidden; /* Ensures the content is not revealed until the animation */
  border-right: .15em solid orange; /* The typwriter cursor */
  white-space: nowrap; /* Keeps the content on a single line */
  margin: 0 auto; /* Gives that scrolling effect as the typing happens */
  letter-spacing: .15em; /* Adjust as needed */
  animation: 
    typing 3.5s steps(30, end),
    blinking-cursor .5s step-end infinite;
}

/* The typing effect */
@keyframes typing {
  from { width: 0 }
  to { width: 100% }
}

/* The typewriter cursor effect */
@keyframes blinking-cursor {
  from, to { border-color: transparent }
  50% { border-color: orange; }
}

A demo of this typewriter effect is available on CodePen.