Typewriter
Text types out character by character with a blinking caret, cycling through several phrases.
CSSJavaScriptBeginner
<h1>We build <span id="t"></span><span class="caret" aria-hidden="true"></span></h1>
h1{margin:0;font-size:clamp(1.6rem,6vw,2.8rem);letter-spacing:-.02em;text-align:center;min-height:1.3em}
.caret{display:inline-block;width:3px;height:1em;margin-left:4px;vertical-align:-.12em;background:#f08a5d;animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
const phrases = ['fast websites', 'smooth animations', 'things people love'];
const el = document.getElementById('t');
let p = 0, i = 0, deleting = false;
function tick() {
const word = phrases[p];
i += deleting ? -1 : 1;
el.textContent = word.slice(0, i);
let delay = deleting ? 35 : 80;
if (!deleting && i === word.length) { deleting = true; delay = 1400; } // pause when finished
else if (deleting && i === 0) { deleting = false; p = (p + 1) % phrases.length; delay = 350; }
setTimeout(tick, delay);
}
tick();
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Typewriter</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
h1{margin:0;font-size:clamp(1.6rem,6vw,2.8rem);letter-spacing:-.02em;text-align:center;min-height:1.3em}
.caret{display:inline-block;width:3px;height:1em;margin-left:4px;vertical-align:-.12em;background:#f08a5d;animation:blink 1s steps(1) infinite}
@keyframes blink{50%{opacity:0}}
</style></head>
<body>
<h1>We build <span id="t"></span><span class="caret" aria-hidden="true"></span></h1>
<script>
const phrases = ['fast websites', 'smooth animations', 'things people love'];
const el = document.getElementById('t');
let p = 0, i = 0, deleting = false;
function tick() {
const word = phrases[p];
i += deleting ? -1 : 1;
el.textContent = word.slice(0, i);
let delay = deleting ? 35 : 80;
if (!deleting && i === word.length) { deleting = true; delay = 1400; } // pause when finished
else if (deleting && i === 0) { deleting = false; p = (p + 1) % phrases.length; delay = 350; }
setTimeout(tick, delay);
}
tick();
</script>
</body></html>
Good for
Hero taglines that rotate between a few short phrases.
Watch out for
Screen readers should get one stable sentence. Put the real text in an aria-label or visually hidden element.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.