Typewriter

Reveal short text character by character while preserving complete accessible text.

Preview

Live preview

Text appearing one character at a time, as if being typed.

Preview

Motion, made legible.

Authoritative vocabulary

Definition and close-term distinctions

English definitions are preserved verbatim from Animation Vocabulary; translations and distinctions help you choose precisely.

Canonical termtypewriter

Typewriter

打字机
Authoritative definition

Text appearing one character at a time, as if being typed.

Chinese translation

文字像正在输入一样逐字符出现。

Browse all 91 terms
Design contract

Decide before this motion ships

Purpose, frequency, trigger, timing, interruptibility, and gesture rules shape the implementation.

Purpose

Reveal short text character by character to simulate typing and pace a narrative moment.

Frequency

Reserve for low-frequency short lines, demos, or brand moments, showing instructional and task text in full.

Trigger

Trigger from explicit playback or one-time scene entry while keeping complete text available in the DOM.

Enter / exit

Reveal characters at a stable pace with optional punctuation pauses, and fade the whole line on exit.

Interruptibility

Reset clearly before replay and reveal the complete text immediately on skip.

Gesture and keyboard

Provide a keyboard-operable skip or reveal control, with the visual caret kept out of focus order.

Reduced-motion strategy

Show the complete text immediately and keep a static caret or remove caret blinking.

Output

Copy into your interface

Current parameters update visual-state CSS, semantic HTML, the required vanilla JS runtime, and an interaction prompt.

Updates with parameters
motion.css
.motion-demo.motion-demo--typewriter {
  position: relative;
  display: grid;
  place-items: center;
  width: min(100%, 34rem);
  min-height: 15rem;
  padding: 2rem;
  overflow: hidden;
  color: #18181b;
  background: #fafafa;
  border: 1px solid #e4e4e7;
  border-radius: 12px;
  isolation: isolate;
}

.motion-demo.motion-demo--typewriter *, .motion-demo.motion-demo--typewriter *::before, .motion-demo.motion-demo--typewriter *::after {
  box-sizing: border-box;
}

.motion-demo.motion-demo--typewriter .motion-surface {
  position: relative;
  display: grid;
  gap: 0.65rem;
  width: min(100%, 18rem);
  padding: 1.25rem;
  background: #ffffff;
  border: 1px solid #e4e4e7;
  border-radius: 8px;
  box-shadow: 0 1px 2px rgb(0 0 0 / 6%), 0 10px 28px rgb(0 0 0 / 7%);
}

.motion-demo.motion-demo--typewriter .motion-line {
  display: block;
  width: 100%;
  height: 0.5rem;
  background: #e4e4e7;
  border-radius: 999px;
}

.motion-demo.motion-demo--typewriter .motion-line:last-child { width: 62%; }

.motion-demo.motion-demo--typewriter .motion-button {
  position: relative;
  min-height: 2.75rem;
  padding: 0.75rem 1.1rem;
  color: white;
  font: inherit;
  font-weight: 700;
  background: #18181b;
  border: 0;
  border-radius: 7px;
  cursor: pointer;
}

.motion-demo.motion-demo--typewriter .motion-button:focus-visible { outline: 3px solid rgb(37 99 235 / 32%); outline-offset: 3px; }

.motion-demo.motion-demo--typewriter .motion-action-row {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 0.85rem;
}

.motion-demo.motion-demo--typewriter .motion-replay-scene {
  width: min(100%, 22rem);
  display: grid;
  place-items: center;
}

.motion-demo.motion-demo--typewriter .motion-pausable-scene {
  width: min(100%, 22rem);
  display: grid;
  place-items: center;
}

.motion-demo.motion-demo--typewriter button.motion-surface {
  color: inherit;
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.motion-demo.motion-demo--typewriter .motion-replay-button,
.motion-demo.motion-demo--typewriter .motion-undo-button,
.motion-demo.motion-demo--typewriter .motion-dismiss-button,
.motion-demo.motion-demo--typewriter .motion-pause-button {
  min-height: 2.75rem;
  padding: 0.65rem 0.9rem;
  color: #18181b;
  font: 650 0.78rem/1 ui-sans-serif, system-ui, sans-serif;
  background: #ffffff;
  border: 1px solid #d4d4d8;
  border-radius: 7px;
  cursor: pointer;
}

.motion-demo.motion-demo--typewriter .motion-replay-button:focus-visible,
.motion-demo.motion-demo--typewriter .motion-undo-button:focus-visible,
.motion-demo.motion-demo--typewriter .motion-dismiss-button:focus-visible,
.motion-demo.motion-demo--typewriter .motion-pause-button:focus-visible,
.motion-demo.motion-demo--typewriter [data-reorder-item]:focus-visible,
.motion-demo.motion-demo--typewriter [data-swipe-target]:focus-visible {
  outline: 3px solid rgb(37 99 235 / 32%);
  outline-offset: 3px;
}

.motion-demo.motion-demo--typewriter .motion-status {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.motion-demo.motion-demo--typewriter .motion-ripple-ink {
  position: absolute;
  width: 12px;
  height: 12px;
  pointer-events: none;
  background: rgb(255 255 255 / 72%);
  border-radius: 50%;
  transform: translate(-50%, -50%) scale(0.95);
  opacity: 0;
}

.motion-demo.motion-demo--typewriter .motion-state-stack { position: relative; width: min(100%, 18rem); height: 9rem; }
.motion-demo.motion-demo--typewriter .motion-state { position: absolute; inset: 0; display: grid; place-items: center; padding: 1rem; background: #ffffff; border: 1px solid #e4e4e7; border-radius: 8px; }
.motion-demo.motion-demo--typewriter .motion-state--to { background: #18181b; color: white; }

.motion-demo.motion-demo--typewriter .motion-scroll-track > span { color: #71717a; font: 600 0.72rem/1.2 ui-monospace, monospace; letter-spacing: 0.08em; text-transform: uppercase; }
.motion-demo.motion-demo--typewriter .motion-orbit-item { display: grid; place-items: center; width: 2.5rem; height: 2.5rem; color: white; background: #2563eb; border-radius: 50%; }
.motion-demo.motion-demo--typewriter .motion-text-stack { position: relative; width: min(100%, 18rem); min-height: 4rem; }
.motion-demo.motion-demo--typewriter svg { width: min(100%, 20rem); height: auto; }

.motion-demo.motion-demo--typewriter .motion-typewriter { position: relative; max-width: 18ch; overflow: hidden; white-space: nowrap; font: 650 1.4rem/1.4 ui-monospace, monospace; animation: motion-typewriter 1200ms steps(18, end) both; }
.motion-demo.motion-demo--typewriter .motion-typewriter::after { content: ""; display: inline-block; width: 2px; height: 1em; margin-left: 0.08em; vertical-align: -0.12em; background: #2563eb; animation: motion-typewriter-caret 700ms step-end infinite; }
@keyframes motion-typewriter { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0); } }
@keyframes motion-typewriter-caret { 50% { opacity: 0; } }

@keyframes motion-typewriter-reduced-fade {
  from { opacity: 0.84; }
  to { opacity: 1; }
}

.force-reduced-motion .motion-demo.motion-demo--typewriter {
  animation: motion-typewriter-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
}

.force-reduced-motion .motion-demo.motion-demo--typewriter *,
.force-reduced-motion .motion-demo.motion-demo--typewriter *::before,
.force-reduced-motion .motion-demo.motion-demo--typewriter *::after {
  animation: none !important;
  animation-delay: 0ms !important;
  animation-iteration-count: 1 !important;
  transition-property: opacity, color, background-color, border-color !important;
  transition-duration: 160ms !important;
  transition-delay: 0ms !important;
  scroll-behavior: auto !important;
}

.force-reduced-motion .motion-demo.motion-demo--typewriter .motion-button,
.force-reduced-motion .motion-demo.motion-demo--typewriter [data-reorder-item],
.force-reduced-motion .motion-demo.motion-demo--typewriter [data-swipe-target],
.force-reduced-motion .motion-demo.motion-demo--typewriter [data-spring-target] {
  transform: none !important;
}

.force-reduced-motion .motion-demo.motion-demo--typewriter .motion-path { stroke-dashoffset: 0 !important; }
.force-reduced-motion .motion-demo.motion-demo--typewriter .motion-word--from { opacity: 0 !important; visibility: hidden; }
.force-reduced-motion .motion-demo.motion-demo--typewriter .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }

@media (prefers-reduced-motion: reduce) {
  .motion-demo.motion-demo--typewriter {
    animation: motion-typewriter-reduced-fade 160ms cubic-bezier(0.23, 1, 0.32, 1) both !important;
  }

  .motion-demo.motion-demo--typewriter *, .motion-demo.motion-demo--typewriter *::before, .motion-demo.motion-demo--typewriter *::after {
    animation: none !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    transition-property: opacity, color, background-color, border-color !important;
    transition-duration: 160ms !important;
    transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }

  .motion-demo.motion-demo--typewriter .motion-button,
  .motion-demo.motion-demo--typewriter [data-reorder-item],
  .motion-demo.motion-demo--typewriter [data-swipe-target],
  .motion-demo.motion-demo--typewriter [data-spring-target] {
    transform: none !important;
  }

  .motion-demo.motion-demo--typewriter .motion-path { stroke-dashoffset: 0 !important; }
  .motion-demo.motion-demo--typewriter .motion-word--from { opacity: 0 !important; visibility: hidden; }
  .motion-demo.motion-demo--typewriter .motion-word:not(.motion-word--from) { opacity: 1 !important; filter: none !important; transform: none !important; }
}
Review notes

Pre-delivery review

  • Expose the complete sentence to screen readers instead of character announcements.
  • Segment by grapheme cluster so emoji and combined characters remain intact.
Accessibility

Keep information accessible

Reduced motion

Show the complete text immediately and keep a static caret or remove caret blinking.

Parameters

Parameter reference

ParameterRange / optionsDefaultPurpose
duration600ms–4000ms1200msUses a longer demo duration so the full change stays observable; immediate production UI feedback should stay under 300ms.
characters6–3618Adjusts characters.
caretOn / OffOnShows a blinking text caret.