These animations affect visual presentation rather than text content. Provide reduced-motion styles in a production page and avoid disruptive cursor blinking.
Multi-line text can be achieved through steps() + width + overflow + white-space.
1. Basic version: single line typing effect
<div class="typewriter">
<p>你好,这是一个打字效果的演示。</p>
</div>.typewriter {
width: fit-content; /* 让容器适应内容 */
font-family: 'Courier New', monospace; /* 等宽字体,便于精确控制 */
}
.typewriter p {
width: 0;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid; /* 模拟光标 */
animation:
typing 4s steps(20, end) forwards, /* 20需替换为实际字符数 */
blink-caret 0.75s step-end infinite;
}
@keyframes typing {
from { width: 0 }
to { width: 40ch } /* 使用ch单位(字符宽度)更精确 */
}
@keyframes blink-caret {
from, to { border-color: transparent }
50% { border-color: black; }
}Principle
The core is:
steps(n)Split the animation into n steps, like characters appearing one by one.
width: 40chch normally uses the advance width of the zero glyph, not every character’s actual width; monospace fonts suit this example better.
2. Width reveal for multiline text
The following width animation reveals text while allowing reflow; it is not true line-by-line or character-by-character output. See the next section for a line-by-line example.
HTML
<div class="typing-multi">
Hello World
This is CSS typing effect
Support multiple lines
Pure CSS animation
</div>CSS
.typing-multi {
width: 30ch;
overflow: hidden;
border-right: 2px solid #000;
white-space: pre-wrap;
line-height: 1.6;
animation:
typing-multi 6s steps(120) forwards,
blink .8s infinite;
}
@keyframes typing-multi {
from {
width: 0;
}
to {
width: 30ch;
}
}
@keyframes blink {
50% {
border-color: transparent;
}
}3. Real multi-line output line by line
If you want output line by line.
HTML
<div class="container">
<p class="line">Hello World</p>
<p class="line">This is CSS typing effect</p>
<p class="line">Support multiple lines</p>
<p class="line">Pure CSS animation</p>
</div>CSS
.line {
width: 0;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid transparent;
}
.line:nth-child(1) {
animation: typing 2s steps(20) forwards, cursor 0.5s step-end 4 alternate;
}
.line:nth-child(2) {
animation: typing 2s steps(25) 2s forwards, cursor 0.5s step-end 4 alternate 2s;
}
.line:nth-child(3) {
animation: typing 2s steps(22) 4s forwards, cursor 0.5s step-end 4 alternate 4s;
}
.line:nth-child(4) {
animation: typing 2s steps(22) 6s forwards, cursor 0.5s step-end infinite alternate 6s;
}
@keyframes typing {
from { width: 0; }
to { width: 100%; }
}
@keyframes cursor {
from { border-right-color: black; }
to { border-right-color: transparent; }
}Effect:
Hello World
↓
This is CSS typing effect
↓
Support multiple lines
↓
Pure CSS animationType line by line.
4. Enterprise-level optimized version (recommended)
Suitable for React/Vue/AI Chat UI.
.typing {
display: inline-block;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid;
width: 0;
animation: typing var(--time) steps(var(--steps)) forwards;
}
@keyframes typing {
to {
width: 100%;
}
}HTML
<div class="typing" style="--time:3s;--steps:30">
Hello this is typing effect
</div>Advantages:
- Dynamically control the number of characters
- Componentization
- Suitable for AI Chat
5. A more realistic solution for AI Chat scenarios
The same effect as ChatGPT / Claude:
CSS + JS
<div id="app">
<div id="text"></div>
</div>#text {
font-size: 18px;
line-height: 1.6;
/* border-right: 2px solid black; */
display: inline-block;
padding-right: 4px;
animation: blink 1s infinite;
white-space: pre-wrap;
}
#text::after {
content: "|";
margin-left: 2px;
animation: blink 1s infinite;
}
@keyframes blink {
50% {
border-color: transparent;
}
}JS control character flow:
function typing(el, text, speed = 40) {
let i = 0
let last = 0
function frame(time) {
if (time - last > speed) {
el.textContent += text[i]
i++
last = time
}
if (i < text.length) {
requestAnimationFrame(frame)
}
}
requestAnimationFrame(frame)
}
const el = document.getElementById("text")
typing(
el,
"Hello World!\nThis is a typing effect demo.\nSupport multiple lines.",
60
)6. Limitations of pure CSS
Pure CSS has several limitations:
| Limitations | Reasons |
|---|---|
| steps cannot be automatically calculated based on character count | CSS does not know text length |
| Difficulty controlling multiple lines | Manual delay required |
| Dynamic text does not work | CSS static |
So AI Chat must be controlled by JS.
7. How to achieve typing effect?
If it is static text, you can use CSS
steps()to achieve:
overflow:hiddenwidthsteps(n)If it is dynamic AI streaming text, JS needs to control the character stream, or directly use SSE/WebSocket's chunk update.