14 CSS Typewriter Effect Designs 06 / 14

CSS Typewriter SVG stroke-dashoffset

SVG text paths are drawn stroke-first using an animated stroke-dashoffset, revealing each letter as a hand-lettered signature — a technique impossible with HTML alone.

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

The code

<div class="tw-06">
  <div class="tw-06__stage">
    <svg class="tw-06__svg" viewBox="0 0 500 160" xmlns="http://www.w3.org/2000/svg">
      <defs>
        <linearGradient id="tw-06-grad" x1="0%" y1="0%" x2="100%" y2="0%">
          <stop offset="0%"   stop-color="#818cf8"/>
          <stop offset="100%" stop-color="#c084fc"/>
        </linearGradient>
      </defs>
      <text class="tw-06__word tw-06__word--1"
        x="50%" y="90" text-anchor="middle"
        font-family="Georgia, serif" font-size="72" font-weight="700"
        stroke="url(#tw-06-grad)" stroke-width="2" fill="url(#tw-06-grad)"
        style="--dash:1400">Signature</text>
    </svg>
    <p class="tw-06__caption">SVG stroke-dashoffset draw technique</p>
  </div>
</div>
.tw-06, .tw-06 *, .tw-06 *::before, .tw-06 *::after { box-sizing: border-box; margin: 0; padding: 0; }
.tw-06 ::selection { background: #818cf8; color: #0a0820; }

.tw-06 {
  --indigo: #818cf8;
  --purple: #c084fc;
  --bg: #0a0820;
  font-family: Georgia, serif;
  background: radial-gradient(ellipse at 50% 60%, #12103a 0%, var(--bg) 70%);
  min-height: 340px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 24px;
}

.tw-06__stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 20px;
  width: 100%;
  max-width: 520px;
}

.tw-06__svg {
  width: 100%;
  height: auto;
  overflow: visible;
  filter: drop-shadow(0 0 20px rgba(129,140,248,0.3));
}

.tw-06__word {
  stroke-dasharray: var(--dash, 1400);
  stroke-dashoffset: var(--dash, 1400);
  fill-opacity: 0;
  animation:
    tw-06-draw 2.4s cubic-bezier(0.4, 0, 0.2, 1) 0.5s forwards,
    tw-06-fill 0.5s ease 2.7s forwards;
}

.tw-06__caption {
  font-size: 0.75rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: #4338ca;
}

@keyframes tw-06-draw {
  from { stroke-dashoffset: var(--dash, 1400); }
  to   { stroke-dashoffset: 0; }
}

@keyframes tw-06-fill {
  from { fill-opacity: 0; }
  to   { fill-opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .tw-06__word { stroke-dashoffset: 0; fill-opacity: 1; animation: none; }
}

How this works

Each SVG <text> element gets stroke-dasharray equal to its total path length, and stroke-dashoffset starts at the same value (fully hidden). Animating stroke-dashoffset to 0 makes the stroke progressively draw itself along the path, revealing the letterforms as if a pen is writing them. The getTotalLength() value is baked in as a CSS custom property for each letter group.

A fill opacity keyframe fires after the stroke animation completes — the fill fades from opacity: 0 to 1 once the outline is drawn, creating the satisfying pop of a freshly inked signature. Letter groups are staggered with animation-delay so they chain one after another in reading order.

Customize

  • Increase stroke width from 2 to 4 for bolder letterforms; pair with a matching wider stroke-dasharray value recalculated from getTotalLength().
  • Add stroke-linecap: round on the text element for calligraphic pen tips at the start and end of each stroke.
  • Try stroke: url(#gradient) with a linear gradient definition to make the drawing stroke change colour as it travels across the word.

Watch out for

  • SVG getTotalLength() returns different values across browsers for the same font and string — bake the value per-browser or over-estimate and accept a tiny gap at letter ends.
  • Variable-width fonts render differently in SVG vs HTML; always test your SVG text in both Chrome and Safari as glyph shapes can differ significantly.
  • SVG text is not searchable or selectable by default — duplicate the text as a visually hidden HTML <span> for screen reader accessibility.

Browser support

ChromeSafariFirefoxEdge
43+ 10+ 16+ 43+

stroke-dashoffset animation is broadly supported; getTotalLength() works in all modern browsers.

Search CodeFronts

Loading…