SolutionsCase StudiesAI ToolsResourcesAboutContact
Get Your Free AI Automation Audit Book a Free 30-Minute Strategy Call
Playground~/frontend Open editor

~/css/typewriter-effects

7 CSS Typewriter Effects

4 Pure CSS3 with JSPublished 2026-10-05Free · no sign-up

Seven typing effects: classic typing with steps(), two lines typed in turn, a headline that types and deletes words, a terminal command with output, a caret blinking after a heading, a typo that gets corrected and a chat bubble that shows typing dots before the message.

01. Classic Typing with steps()

TypewriterNo JS

The text reveals one character at a time because its width animates in steps() from 0 to the text's length in ch units, and a border on the right blinks as the caret. It needs a monospace font so every character is exactly 1ch wide.

View code
<p class="ty1">npm run build</p>
.ty1 { width: 13ch; margin: 0; overflow: hidden; white-space: nowrap; border-right: 3px solid #4ade80; color: #4ade80; font: 600 24px "JetBrains Mono", ui-monospace, monospace; animation: ty1t 2s steps(13) .4s both, ty1c .75s step-end infinite; }
@keyframes ty1t { from { width: 0; } }
@keyframes ty1c { 50% { border-color: transparent; } }
@media (prefers-reduced-motion: reduce) { .ty1 { animation: none; } }

02. Two Lines Typed in Turn

TypewriterSequence

The second line starts typing only after the first has finished, using an animation delay equal to the first line's duration. The caret moves with the typing because only the line currently being typed shows it.

View code
<div class="ty2"><p class="a">Hello, I'm Sam.</p><p class="b">I build fast shops.</p></div>
.ty2 { font: 600 22px "JetBrains Mono", ui-monospace, monospace; color: #101828; }
.ty2 p { width: 0; margin: 0 0 6px; overflow: hidden; white-space: nowrap; border-right: 3px solid transparent; }
.ty2 .a { animation: ty2a 1.5s steps(15) .3s forwards, ty2on 1.8s step-end; }
.ty2 .b { color: #7c3aed; animation: ty2b 1.9s steps(19) 2s forwards, ty2c .7s step-end 2s infinite; }
@keyframes ty2a { to { width: 15ch; } }
@keyframes ty2b { to { width: 19ch; } }
@keyframes ty2on { 0%, 99% { border-color: currentColor; } }
@keyframes ty2c { 0%, 49% { border-color: currentColor; } }
@media (prefers-reduced-motion: reduce) { .ty2 .a { width: 15ch; animation: none; } .ty2 .b { width: 19ch; animation: none; } }

03. Type, Delete and Loop Words

TypewriterJavaScript

A headline types a word, pauses, deletes it and types the next, in a loop. Screen readers get one static sentence listing every word, and the moving part is aria-hidden, because a constantly changing word would be read over and over.

View code
<h3 class="ty3"><span class="sr">We design websites, online shops and mobile apps.</span><span aria-hidden="true">We design <span class="w"></span><span class="c">|</span></span></h3>
.ty3 { margin: 0; color: #f8fafc; font: 800 26px Inter, system-ui, sans-serif; }
.ty3 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.ty3 .w { color: #38bdf8; }
.ty3 .c { color: #38bdf8; font-weight: 400; animation: ty3 .8s step-end infinite; }
@keyframes ty3 { 50% { opacity: 0; } }
var el = document.querySelector('.ty3 .w'), words = ['websites', 'online shops', 'mobile apps'], i = 0, n = 0, del = false;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) { el.textContent = words[0]; } else (function tick() {
  var w = words[i]; n += del ? -1 : 1; el.textContent = w.slice(0, n);
  var wait = del ? 50 : 100;
  if (!del && n === w.length) { del = true; wait = 1400; } else if (del && n === 0) { del = false; i = (i + 1) % words.length; wait = 300; }
  setTimeout(tick, wait);
})();

04. Terminal Command and Output

TypewriterTerminal

A command is typed at the prompt, then its output lines appear one by one, like a real terminal session. The typing uses steps() and the output lines fade in with delays that start after the typing ends.

View code
<div class="ty4"><p class="cmd"><span class="pr">$</span> <span class="t">git push origin main</span></p><p class="o" style="--d:2.4s">Enumerating objects: 12, done.</p><p class="o" style="--d:2.8s">Writing objects: 100% (7/7)</p><p class="o ok" style="--d:3.2s">main -&gt; main</p></div>
.ty4 { width: 300px; max-width: 100%; padding: 14px; box-sizing: border-box; border-radius: 12px; background: #0b1020; font: 500 13px/1.7 "JetBrains Mono", ui-monospace, monospace; color: #cbd5e1; }
.ty4 p { margin: 0; }
.ty4 .pr { color: #a78bfa; }
.ty4 .t { display: inline-block; width: 0; vertical-align: bottom; overflow: hidden; white-space: nowrap; color: #f8fafc; animation: ty4 2s steps(20) .3s forwards; }
.ty4 .o { opacity: 0; animation: ty4o .01s var(--d) forwards; }
.ty4 .ok { color: #4ade80; }
@keyframes ty4 { to { width: 20ch; } }
@keyframes ty4o { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ty4 .t { width: 20ch; animation: none; } .ty4 .o { opacity: 1; animation: none; } }

05. Blinking Caret After a Heading

TypewriterCaret

No typing at all, just a block caret blinking after a finished heading, which gives a code-themed brand a hint of a terminal without making anyone wait to read. The caret is a decorative pseudo-element.

View code
<h3 class="ty5">build something small</h3>
.ty5 { margin: 0; color: #101828; font: 700 26px "JetBrains Mono", ui-monospace, monospace; }
.ty5::after { content: ""; display: inline-block; width: .55em; height: 1em; margin-left: 4px; vertical-align: -.12em; background: #7c3aed; animation: ty5 1s step-end infinite; }
@keyframes ty5 { 50% { opacity: 0; } }
@media (prefers-reduced-motion: reduce) { .ty5::after { animation: none; } }

06. Typo, Delete and Correct

TypewriterJavaScript

The line types a wrong word, pauses, deletes it and types the right one, a human touch for a hero or a cover letter site. A small script plays a list of type and delete steps; the finished sentence is in the HTML for screen readers and no-script visitors.

View code
<p class="ty6"><span class="sr">Websites that feel fast.</span><span class="v" aria-hidden="true">Websites that feel fast.</span></p>
.ty6 { margin: 0; color: #101828; font: 700 24px "JetBrains Mono", ui-monospace, monospace; }
.ty6 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.ty6 .v::after { content: "▍"; color: #e11d48; animation: ty6 .8s step-end infinite; }
@keyframes ty6 { 50% { opacity: 0; } }
var v = document.querySelector('.ty6 .v');
if (!matchMedia('(prefers-reduced-motion: reduce)').matches) {
  var steps = [['t', 'Websites that feel slow'], ['w', 600], ['d', 4], ['t', 'fast.']], out = '', k = 0;
  v.textContent = '';
  (function run() {
    var s = steps[k]; if (!s) return;
    if (s[0] === 'w') { k++; setTimeout(run, s[1]); return; }
    if (s[0] === 't') { if (s.n === undefined) s.n = 0; out += s[1][s.n++]; v.textContent = out; if (s.n >= s[1].length) k++; return setTimeout(run, 70); }
    if (s[0] === 'd') { if (s.n === undefined) s.n = s[1]; out = out.slice(0, -1); v.textContent = out; if (--s.n <= 0) k++; return setTimeout(run, 60); }
  })();
}

07. Typing Indicator Then Message

TypewriterChat

A chat bubble shows three bouncing dots while the other person types, then the message replaces them. The dots are aria-hidden with a visually hidden Typing label, and the final message lands in a polite live region so it is announced.

View code
<div class="ty7" aria-live="polite"><div class="b typing"><span class="sr">Support is typing</span><i aria-hidden="true"></i><i aria-hidden="true"></i><i aria-hidden="true"></i></div></div>
.ty7 { width: 260px; max-width: 100%; font: 15px Inter, system-ui, sans-serif; }
.ty7 .b { display: inline-flex; gap: 5px; padding: 12px 14px; border-radius: 16px 16px 16px 4px; background: #1e293b; color: #e2e8f0; line-height: 1.4; }
.ty7 i { width: 8px; height: 8px; border-radius: 50%; background: #94a3b8; animation: ty7 1.2s ease-in-out infinite; }
.ty7 i:nth-of-type(2) { animation-delay: .15s; } .ty7 i:nth-of-type(3) { animation-delay: .3s; }
.ty7 .sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
@keyframes ty7 { 0%, 60%, 100% { transform: none; opacity: .5; } 30% { transform: translateY(-5px); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .ty7 i { animation: none; } }
var w = document.querySelector('.ty7');
setTimeout(function () { w.innerHTML = '<div class="b">Hi! Your order shipped this morning.</div>'; }, 2600);

About these CSS Typewriter Effects

steps() and ch units

In a monospace font every character is exactly 1ch wide. Animate the width from 0 to the text's length in ch with steps(n), where n is the number of characters, and each step reveals one letter.

The caret is a border

A right border on the typed element makes a caret that moves with the text. Blink it with a separate step-end animation that turns the border colour transparent half the time.

Static text for screen readers

Changing text is read out again and again, or read half-typed. Keep the finished sentence in visually hidden text and hide the animated copy with aria-hidden.

Do not make people wait

Typing delays reading. Keep typed text short, start quickly and under prefers-reduced-motion show the finished text straight away.

Frequently asked questions

Can I type proportional fonts?

Not accurately with steps() and ch. Use the JavaScript demos, which add letters one by one, for any font.

How do I loop the CSS version?

Add infinite and alternate to the typing animation; it will type and then untype.

Is a typewriter effect accessible?

With a static copy for screen readers and a reduced-motion fallback, yes.