14 CSS Typewriter Effect Designs 05 / 14

CSS Typewriter clip-path Reveal

Text is revealed character-by-character using an animated clip-path inset that travels left-to-right — giving a hard-edged paint-stroke reveal instead of the usual overflow trick.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="tw-05">
  <div class="tw-05__stage">
    <div class="tw-05__eyebrow">Award-winning studio</div>
    <h1 class="tw-05__heading">
      <span class="tw-05__line tw-05__line--1">Craft that</span>
      <span class="tw-05__line tw-05__line--2">moves people.</span>
    </h1>
    <div class="tw-05__cursor"></div>
  </div>
</div>
.tw-05, .tw-05 *, .tw-05 *::before, .tw-05 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.tw-05 ::selection { background: #ec4899; color: #1a0010; }

.tw-05 {
  --pink: #ec4899;
  --rose: #fb7185;
  --bg: #0d0008;
  --text: #fdf2f8;
  --muted: #831843;
  font-family: 'Georgia', 'Times New Roman', serif;
  min-height: 340px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 48px 32px;
}

.tw-05__stage {
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 480px;
}

.tw-05__eyebrow {
  font-size: 0.72rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--pink);
  clip-path: inset(0 100% 0 0);
  animation: tw-05-reveal 0.6s steps(1) 0.3s forwards;
}

.tw-05__heading {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.tw-05__line {
  display: block;
  position: relative;
  font-size: clamp(2rem, 6vw, 3.8rem);
  font-weight: 700;
  color: var(--text);
  line-height: 1.1;
  clip-path: inset(0 100% 0 0);
  overflow: hidden;
}

.tw-05__line::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(90deg, var(--pink), var(--rose));
  opacity: 0.25;
  clip-path: inset(0 100% 0 0);
}

.tw-05__line--1 {
  animation: tw-05-reveal 0.9s steps(10) 0.8s forwards;
}
.tw-05__line--1::before {
  animation: tw-05-reveal 0.9s steps(10) 0.6s forwards;
}

.tw-05__line--2 {
  animation: tw-05-reveal 1.1s steps(13) 2s forwards;
}
.tw-05__line--2::before {
  animation: tw-05-reveal 1.1s steps(13) 1.8s forwards;
}

.tw-05__cursor {
  width: 3px;
  height: 2.4rem;
  background: var(--pink);
  box-shadow: 0 0 12px var(--pink);
  animation: tw-05-blink 0.8s steps(2) 2.8s infinite;
  opacity: 0;
}

@keyframes tw-05-reveal {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0% 0 0); }
}

@keyframes tw-05-blink {
  0%,100% { opacity: 1; }
  50%     { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .tw-05__eyebrow, .tw-05__line, .tw-05__line::before { clip-path: none; animation: none; }
  .tw-05__cursor { opacity: 1; animation: none; }
}

How this works

Instead of animating width, the text is full-width from the start but masked by clip-path: inset(0 100% 0 0). Animating the second value from 100% to 0% with steps(N) timing peels the mask away from left to right, one character-width at a time. This approach works with proportional fonts and variable-width containers — no ch counting required.

A separate highlight layer, an absolutely-positioned ::before pseudo-element with the same clip-path animation but a slight delay, creates a sweeping colour wash effect that advances just ahead of the revealed text — mimicking a highlighter pen uncovering the words. The two animations share the same duration and step count so they stay locked in sync.

Customize

  • Change the reveal direction to right-to-left by animating clip-path: inset(0 0 0 100%) to inset(0 0 0 0).
  • Add a vertical reveal (top-to-bottom) by using inset(0 0 100% 0) to inset(0 0 0 0) for dramatic line-by-line reveals on large headings.
  • Combine with animation-delay stagger on multiple lines to create a staggered paint-stroke entrance for a whole paragraph.

Watch out for

  • clip-path animations with steps() may produce visible aliasing on sub-pixel boundaries — use will-change: clip-path and ensure GPU compositing is active.
  • The cursor element needs to be positioned absolutely and move in sync with the clip reveal; it cannot rely on normal text flow once clip-path is applied.
  • Safari before 15.4 has inconsistent clip-path: inset() animation interpolation — test carefully and provide a width-based fallback.

Browser support

ChromeSafariFirefoxEdge
55+ 13.1+ 54+ 55+

clip-path animation on inset() requires Safari 13.1+; use @supports for progressive enhancement.

Search CodeFronts

Loading…