All Taste components
Text effects
Typewriter
A terminal line that types a command with a blinking caret, shows the result, then starts again.
type · hold · erase · caret · output line
$ npx taste add hero
Added components/hero.tsx
"use client";
/* globals.css
@keyframes caret { to { visibility: hidden; } }
*/
import { useEffect, useState } from "react";
interface TypewriterProps {
lines: string[];
/** Milliseconds per character. */
speed?: number;
}
export function Typewriter({ lines, speed = 55 }: TypewriterProps) {
const [line, setLine] = useState(0);
const [count, setCount] = useState(0);
const [erasing, setErasing] = useState(false);
const text = lines[line];
useEffect(() => {
// Reduced motion: show the whole first line, no typing.
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
setCount(text.length);
return;
}
const full = count === text.length;
const delay = !erasing && full ? 1600 : erasing && count === 0 ? 300 : erasing ? speed / 2 : speed;
const t = setTimeout(() => {
if (!erasing && full) setErasing(true);
else if (erasing && count === 0) {
setErasing(false);
setLine((l) => (l + 1) % lines.length);
} else setCount((c) => c + (erasing ? -1 : 1));
}, delay);
return () => clearTimeout(t);
}, [count, erasing, text, lines.length, speed]);
return (
<div className="w-full max-w-sm overflow-hidden rounded-lg border border-border bg-surface font-mono text-[13px] leading-relaxed">
<div aria-hidden className="flex gap-1.5 border-b border-border px-3 py-2.5">
<i className="size-2 rounded-full bg-border-strong" />
<i className="size-2 rounded-full bg-border-strong" />
<i className="size-2 rounded-full bg-border-strong" />
</div>
<p className="px-3.5 py-3 text-fg" aria-label={text}>
<span aria-hidden className="text-fg-subtle">$ </span>
<span aria-hidden>{text.slice(0, count)}</span>
<span aria-hidden className="ml-px inline-block h-[1.15em] w-[7px] translate-y-[3px] bg-primary animate-[caret_1s_steps(2,start)_infinite] motion-reduce:animate-none" />
</p>
</div>
);
}
// <Typewriter lines={["npx taste add hero", "npx taste add pricing"]} />
The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.