Typewriter Heading
Overview
The keyword at the end of the headline types out, pauses and deletes, cycling through several words. Blinking caret; characters appear with opacity, no jitter.
Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Typewriter Heading to your project in your colors.
Where to use it
Animated headlines, testimonials, emphasis copy and large-type layouts.
Install
With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.
$ npm i -g @8pt-dev/cli && 8pt add typewriter-heading
mcp › Use 8pt MCP to call get_component("typewriter-heading")
Code
Source for Typewriter Heading. Motion uses only transform and opacity and respects the Reduce motion setting.
<div class="x-type"> <small>8pt Components</small> <h3>For<span class="x-type__w" aria-live="polite"></span><span class="x-type__caret" aria-hidden="true"></span><br> </h3> </div>
.x-type{display:grid;gap:10px;color:var(--d-fg,#fff);text-align:left;width:min(320px,100%)}
.x-type small{font:600 11px/1 ui-monospace,monospace;letter-spacing:.12em;color:var(--d-muted,#9b9ba8)}
.x-type h3{margin:0;font:800 clamp(30px,5vw,44px)/1.1 var(--d-font,system-ui,sans-serif);letter-spacing:-.04em}
.x-type__w{margin-left:.15em;background:linear-gradient(90deg,var(--d-accent,#4D74FF),#9ef0ff);-webkit-background-clip:text;background-clip:text;color:transparent}
.x-type__w i{font-style:normal;animation:x-type-in .12s ease-out}
@keyframes x-type-in{from{opacity:0}}
.x-type__caret{display:inline-block;width:3px;height:.9em;margin-left:4px;vertical-align:-.08em;border-radius:2px;background:var(--d-accent,#4D74FF);animation:x-type-blink 1s steps(1) infinite}
@keyframes x-type-blink{50%{opacity:0}}const init = (root) => {
const w = root.querySelector('.x-type__w');
const words = ['Designer', 'Claude Code', 'Small teams', 'Ships next week'];
let wi = 0, ci = 0, del = false, timer;
if (matchMedia('(prefers-reduced-motion: reduce)').matches) { w.textContent = words[0]; return; }
const tick = () => {
const s = words[wi];
if (!del) { ci++; w.innerHTML = [...s.slice(0, ci)].map(c => `<i>${c === ' ' ? ' ' : c}</i>`).join(''); if (ci === s.length) { del = true; timer = setTimeout(tick, 1400); return; } timer = setTimeout(tick, 90); }
else { ci--; w.lastElementChild && w.lastElementChild.remove(); if (ci === 0) { del = false; wi = (wi + 1) % words.length; timer = setTimeout(tick, 300); return; } timer = setTimeout(tick, 45); }
};
timer = setTimeout(tick, 400);
return () => clearTimeout(timer);
…Subscribe to Builder to see the full source.See pricing