TextFree

Typing Effect

Pure CSS animation that reveals text character by character behind a blinking indigo cursor — zero JavaScript, a single <span>.

CSSAnimationTypewriter
Hello World
Hover or click the scene to interact

This effect is free — the Text category contains 56 effects total, including 4 free. Effect.Labs has 811 vanilla effects. Explore the category →

Usage examples

I'm a

Developer

Crafting fast, accessible web experiences since 2018.

① Portfolio / landing — Role typed beneath the name

WhenThe hero section of a portfolio or SaaS landing displays a fixed name as a large heading, with the role being typed just below it.
WhyThe animated width sits naturally inside an <code>h2</code> or paragraph — the blinking indigo cursor draws the eye without competing with the main CTA. The write/erase loop implies multiple roles without any JS.
Settingssteps(11) for 'Developer' (9 chars), 4s duration for comfortable reading, font-size: 2rem, brand-colored cursor.
Terminal
~/projet $  npm install effect-libs
added 1 package in 0.312s added 1 package in 0.312s
found 0 vulnerabilities 0 vulnérabilités détectées

② Technical doc — npm command typed in a fake terminal

WhenA documentation page or onboarding screen shows an install command being typed in real time inside a simulated terminal, before output appears.
WhyThe stepped rendering precisely mimics keyboard input; with a monospace font and dark background, the span becomes a credible terminal prompt with no images or libraries needed.
SettingsMonospace font, font-size: 1rem, font-weight: 400, steps(30) for a longer command, white cursor (border-right-color: #e2e8f0), 3s duration.
AI
AI Assistant
How do I center a div in CSS?
AI
Use display:flex on the parent.
Ask anything…

③ AI interface — Response generated character by character

WhenIn a chat or AI assistant UI, the response appears letter by letter, mimicking an LLM's token streaming and creating a natural sense of live generation.
WhyThe CSS typing effect visually reproduces token-by-token streaming from a language model with zero JavaScript — the blinking indigo cursor reinforces the perception of active processing and holds the user's attention.
Settingsfont-size: 0.88rem, font-weight: 400, steps(30) for a short message (~30 chars), standard indigo cursor (border-right: 2px solid rgb(99, 102, 241)), 4s duration.

How it works

The effect relies entirely on two CSS @keyframes with no JavaScript whatsoever. The width property starts at 0 and animates to 100% using the steps(20) timing function: instead of a smooth interpolation, the browser jumps through 20 discrete increments, simulating letter-by-letter appearance. overflow: hidden hides excess text to the right until each step is reached; white-space: nowrap prevents line breaks that would break the illusion.

The typing animation follows a plateau curve: from 0% to 30% of the cycle, the width grows from 0 to 100% (typing); it holds full from 30% to 60% (readable pause); then from 60% to 90% it returns to 0 (erasure, same steps(20) reversed). From 90% to 100%, width stays at 0 — a micro-pause before the next typing cycle.

The cursor is simulated by border-right: 3px solid rgb(99, 102, 241) set directly on the <span>no pseudo-element, no extra DOM node. The blink animation (0.5 s, step-end timing, alternate direction) switches the border color between indigo and transparent: the instant jump of step-end reproduces the square blink of a real terminal cursor, with no fade.

max-width: fit-content prevents the span from stretching across the full line when inside a wide flex or grid container — without it, the cursor border would appear at the right of the block, far from the last character. Both animations loop infinitely and independently on the same element.

Accessibility

  • prefers-reduced-motion absent: the source code contains no prefers-reduced-motion media query. The animation runs continuously even for motion-sensitive users. Recommended fix: add @media (prefers-reduced-motion: reduce) { .text-typing { animation: none; width: 100%; border-right: none; } }.
  • The <span> holds real text read normally by screen readers — no information is encoded in the animation itself.
  • No aria-live attribute on the element: if the text changes dynamically via JS (phrase cycling), add aria-live="polite" so updates are announced.
  • No interactive element in the effect — keyboard navigation is not affected by this effect itself.
  • Cursor color (rgb(99, 102, 241) — indigo-500) achieves a ratio ≥ 4.5:1 on dark backgrounds (#0a0a0f); text color inherits from the integration context, whose contrast is the integrator's responsibility.

Browser compatibility

Pure CSS effect — @keyframes, steps(), and max-width: fit-content cover the modern browser landscape. Zero dependencies.

Chrome 46+✓ Full
Firefox 94+✓ Full
Safari 11+✓ Full
Edge 79+✓ Full
Mobile iOS 11+✓ Full
Android Chrome 46+✓ Full

On Firefox before 94, <code>max-width: fit-content</code> is not recognized: the cursor border may stretch visually, without blocking text readability. Without <code>@keyframes</code> support, text displays fully and statically — no JS errors, content always readable.

The code

Copy the three blocks into your page. No dependencies.

HTML
<span class="demo-text text-typing">Hello World</span>
CSS
.demo-text  {
   font-size: 2.5rem;
   font-weight: 800;
   letter-spacing: -0.02em;
   }

.text-typing  {
   overflow: hidden;
   border-right: 3px solid rgb(99, 102, 241);
   white-space: nowrap;
   animation: 3s steps(20) 0s infinite normal none running typing, 0.5s step-end 0s infinite alternate none running blink;
   width: 0px;
   max-width: fit-content;
   }

@keyframes typing  {
   
  0%, 90%, 100%  {
   width: 0px;
   }
  30%, 60%  {
   width: 100%;
   }
}

@keyframes blink  {
   
  50%  {
   border-color: transparent;
   }
}
JavaScript (fx-0630)
// Effet CSS pur — pas de JavaScript nécessaire

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
steps(20) 20 Number of discrete typing animation steps — should approximate the character count of the text (spaces included). Too low: chunky jumps; too high: nearly smooth, loses the typewriter feel.
3s (typing duration) 3s Total cycle duration (typing + hold + erasure). Increase to 4–6 s for long text or a comfortable reading pace.
0.5s (blink duration) 0.5s Cursor blink speed. 0.5 s = classic terminal; 1 s = slow cursor; 0.3 s = nervous, fast cursor.
border-right: 3px solid rgb(99, 102, 241) 3px, indigo-500 Cursor thickness and color. Swap the color for your brand hue. 4 px gives an IDE-style block cursor; 2 px gives a thin editorial cursor.
font-size: 2.5rem (.demo-text) 2.5rem Size of the animated text. Large hero: 3–4 rem; subtitle: 1–1.5 rem; terminal: 0.9–1.1 rem.
font-weight: 800 (.demo-text) 800 Text weight. 400 for body or terminal text; 700 for a standard heading; 900 for maximum impact.
letter-spacing: -0.02em (.demo-text) -0.02em Character spacing. Negative = tight (modern, bold style); 0 or slightly positive = open (editorial, body text).

FAQ

How do I match the steps() value to my text?

Count the characters in your text, including spaces, and use a close value. For 'Hello World' (11 chars), steps(11) to steps(14) is natural. Too few steps produce visible jumps between character groups; too many make the progression nearly continuous, losing the typewriter feel.

How can I cycle through multiple different texts?

The CSS effect alone loops a fixed text. To rotate between phrases, two approaches: (1) CSS only — stack <span class="text-typing"> elements with position: absolute and animation-delay values offset by multiples of the cycle duration; (2) light JS — listen for the animationiteration event on the span and update its textContent between cycles.

Does Typing Effect work on a light background?

Yes. The effect sets no background or text color. Only adjust border-right-color to keep the cursor visible against your background (e.g. #1e293b on white). Text color inherits from your stylesheet; no background property is defined by the effect itself.