CSSで文字を動かす方法は1つではありません。ふわっと表示するのか、1文字ずつ見せるのか、タイピングのように横へ広げるのかで実装が変わります。まず目的を分けると、必要なCSSがかなりシンプルになります。
まず使い分け:文字アニメーションは目的で選ぶ
| 見せ方 | 主なCSS | 向いている場面 |
|---|---|---|
| フェード | opacity + @keyframes | 見出しや短いコピーを自然に表示 |
| 1文字ずつ | span + animation-delay | ロゴや短いキャッチコピー |
| タイピング風 | width + steps() | 固定長の短いテキストを段階表示 |
動的に変わる長文や、ユーザー操作に応じて複雑に順序を変える場合は、CSSだけに寄せずJavaScript側で状態を管理した方が保守しやすいことがあります。
(@keyframesとanimationの基本から確認したい場合については『【簡単に試せる】CSSアニメーションの仕組みを徹底解説!初心者でもわかる動きの作り方』をご参照ください)
フェード表示はopacityと@keyframesで作る
最も扱いやすいのは、透明度を変えるフェードです。@keyframesで開始と終了の状態を定義し、対象要素へanimationを指定します。
<p class="fade-text">CSSで文字を表示する</p>
<style>
.fade-text {
opacity: 0;
animation: fade-in 700ms ease-out forwards;
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(8px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
forwardsを付けると、アニメーション終了後も最終キーフレームの見た目を維持できます。繰り返しが不要な文章では、まず1回だけの表示から始めると調整しやすくなります。
1文字ずつ表示するなら文字を要素へ分けて遅延させる
各文字の開始時刻をずらしたい場合は、文字ごとにspanへ分けてanimation-delayを変えます。固定コピーならCSSだけでも管理できます。
<p class="char-text" aria-label="HELLO">
<span class="char" aria-hidden="true">H</span>
<span class="char" aria-hidden="true">E</span>
<span class="char" aria-hidden="true">L</span>
<span class="char" aria-hidden="true">L</span>
<span class="char" aria-hidden="true">O</span>
</p>
<style>
.char {
display: inline-block;
opacity: 0;
animation: char-in 400ms ease-out forwards;
}
.char:nth-child(2) { animation-delay: 80ms; }
.char:nth-child(3) { animation-delay: 160ms; }
.char:nth-child(4) { animation-delay: 240ms; }
.char:nth-child(5) { animation-delay: 320ms; }
@keyframes char-in {
from { opacity: 0; transform: translateY(6px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
文字数が頻繁に変わる場合、nth-childを手で増やし続けるのは負担です。その場合は、JavaScriptやテンプレート側で文字要素と遅延値を生成する方が向いています。
タイピング風はsteps()で段階的に幅を広げる
steps()は、アニメーション進行を指定した数の区間に分割します。幅の変化と組み合わせると、文字が段階的に現れるタイピング風表現を作れます。
<p class="typing">CSS animation</p>
<style>
.typing {
width: 13ch;
overflow: hidden;
white-space: nowrap;
border-right: 2px solid currentColor;
animation:
typing 2s steps(13, end) forwards,
caret 700ms step-end infinite;
}
@keyframes typing {
from { width: 0; }
to { width: 13ch; }
}
@keyframes caret {
50% { border-color: transparent; }
}
</style>
この方法は固定文字列ほど扱いやすく、文字数とsteps()の値、幅の指定をそろえる必要があります。フォントや文字種によって見え方が変わるため、実際の表示環境で確認してください。
動きを減らす設定を用意する
文字の移動や点滅は、利用者によっては負担になります。prefers-reduced-motionで動きの抑制を希望している場合は、必須ではないアニメーションを停止または弱める設計ができます。
@media (prefers-reduced-motion: reduce) {
.fade-text,
.char,
.typing {
animation: none;
opacity: 1;
transform: none;
width: auto;
border-right: 0;
}
}
アニメーションそのものが情報伝達に必須でないなら、動かない状態でも内容を読めることを優先します。
CSSだけで足りないケースを見分ける
- 文字列がAPI結果などで毎回変わる
- ユーザー操作で再生・停止・順序を細かく制御する
- 複数要素の状態を同期させる
- アニメーション完了を次の処理条件として使う
このような場合はJavaScriptで状態を持ち、CSSは見た目の変化を担当させると責務を分けやすくなります。
(ほかのCSSアニメーション実装例も確認したい場合については『【保存版】Web制作で使えるCSSアニメーション10選|サンプルコード&使い方解説』をご参照ください)
(スクロール位置と連動させたい場合については『CSSスクロールアニメーション完全解説|scroll-timelineでJS不要の軽量演出を実現』をご参照ください)
まとめ:まず見せ方を決めてからCSSを選ぶ
- 自然に出すならopacity中心のフェード
- 短い固定コピーなら文字ごとのdelay
- タイピング風ならsteps()と幅の組み合わせ
- 動的制御が増えたらJavaScriptへ役割を分ける
- prefers-reduced-motionで動きを減らせるようにする
まずは短い見出し1つでフェードを動かし、次に1文字ずつ・タイピング風へ広げると違いを確認しやすくなります。