WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

CSSタイピングアニメーションの作り方|コピペ実装集

生徒

チャットやターミナルみたいに、文字が1文字ずつ打たれる演出ってCSSで作れますか?

ペン博士

作れるよ!steps()関数とwidthアニメーション、それにカーソルの点滅を組み合わせるのがコツ。今日はコピペで動くパターンを用意したよ。

ターミナルやチャット画面のように「文字が1文字ずつ打ち込まれていく」タイピングアニメーション。Webサイトのヒーローセクションや404ページなど、さまざまな場所で見かけます。

実はこの演出、JavaScriptを使わずCSSだけで実装できます。ただし「なぜこの書き方なのか」を理解せずにコピペすると、文字数が変わったときに壊れたり、日本語でうまく動かなかったりというトラブルが起きます。

この記事では、CSSタイピングアニメーションの仕組みから丁寧に解説したうえで、コピペで使える実装パターンを複数用意しています。ループさせる方法、複数行に対応する方法、日本語での注意点、そしてCSSの限界とJavaScript1行で解決する方法まで網羅します。

  • steps() 関数とwidthアニメーションでタイプライター効果を作る仕組み
  • border-right でカーソル点滅を実装する方法
  • ループ(繰り返し)させるための @keyframes 設計
  • 複数行のタイピングアニメーションを実装する方法
  • 日本語テキストで ch 単位が使えない理由と対処法
  • CSSの限界と JavaScript 1行で動的対応する方法

目次

CSSタイピングアニメーションの仕組み

タイピング表現の仕組み

タイピングアニメーションは「width を 0 から 最終幅まで広げる」ことで実現します。テキストは overflow: hidden で隠しておき、widthが広がるにつれて左から文字が見えてくるという原理です。

ポイントはsteps() イージング関数を使うことです。通常の animation は滑らかに変化しますが、steps(N) を使うと N 段階のステップ状の変化になります。これにより「1文字ずつぱっぱっと現れる」タイプライター感が生まれます。

steps() の動作を理解する

/* 滑らかに広がる(タイピングっぽくない) */
.smooth {
  animation: expand 3s linear forwards;
}

/* 段階的に広がる(タイピング風) */
.typewriter {
  animation: expand 3s steps(20, end) forwards;
  /* steps(文字数, end) と書く */
}

@keyframes expand {
  from { width: 0; }
  to   { width: 20ch; } /* 20文字分の幅 */
}

steps(20, end) の 20 は「アニメーション全体を20段階に分ける」という意味です。テキストが20文字なら steps(20) と書くことで、1ステップにつき1文字分進むように合わせます。


基本のタイピングアニメーション実装

実装の4ステップ

英数字1行版(最もシンプル)

.typewriter {
  font-family: monospace; /* 等幅フォント必須 */
  white-space: nowrap;    /* 折り返し禁止 */
  overflow: hidden;       /* はみ出し非表示 */
  width: 0;
  animation: typing 3s steps(30, end) forwards;
}

@keyframes typing {
  to {
    width: 30ch; /* テキストの文字数に合わせる */
  }
}
<!-- 「Hello, WithCode!」は15文字 → steps(15)、width: 15ch -->
<p class="typewriter" style="animation: typing 2s steps(15, end) forwards;">
  Hello, WithCode!
</p>

ch 単位は等幅フォントの「0」1文字分の幅を表します。等幅フォント(monospace)では全文字が同じ幅なので、「文字数ch = テキストの全幅」が成り立ちます。

カーソル点滅を追加する

border-right でカーソルを疑似的に表現し、animation で点滅させます。

.typewriter-cursor {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  border-right: 2px solid #333; /* これがカーソル */
  animation:
    typing 3s steps(20, end) forwards,
    blink 0.75s step-end infinite;
}

@keyframes typing {
  to {
    width: 20ch;
  }
}

@keyframes blink {
  0%, 100% { border-color: #333; }  /* 表示 */
  50%       { border-color: transparent; } /* 非表示 */
}
<h1 class="typewriter-cursor">Welcome to WithCode</h1>

blink アニメーションに step-end を使うことで、滑らかにフェードするのではなく「パッ・パッ」とオン/オフが切り替わるカーソルらしい点滅になります。


タイピング完了後にカーソルを止める・消す

タイピングが終わった後、カーソルを点滅させ続けることもできますし、数秒後に消すこともできます。

/* タイピング完了後もカーソル点滅を続ける */
.typewriter-keep-cursor {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  border-right: 2px solid currentColor;
  animation:
    typing 3s steps(18, end) forwards,
    blink 0.75s step-end infinite; /* infinite で永続 */
}

/* タイピング完了後にカーソルを消す */
.typewriter-hide-cursor {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  border-right: 2px solid currentColor;
  animation:
    typing 3s steps(18, end) forwards,
    blink 0.75s step-end 4, /* 4回だけ点滅(約3秒) */
    hideCursor 0s 3.2s forwards; /* タイピング後に消す */
}

@keyframes hideCursor {
  to {
    border-color: transparent;
  }
}

ループ(繰り返し)タイピングアニメーション

文字が打ち込まれてから消えて、また打ち込まれる……というループを作るには、@keyframes で「広がる → 縮む」の動きを定義します。

.typewriter-loop {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  border-right: 2px solid #333;
  width: 0;
  animation:
    typingLoop 4s steps(20, end) infinite,
    blink 0.75s step-end infinite;
}

@keyframes typingLoop {
  /* 0〜40%:文字を打ち込む */
  0%   { width: 0; }
  40%  { width: 20ch; }
  /* 40〜60%:打ち終わった状態でカーソルを点滅させて待機 */
  60%  { width: 20ch; }
  /* 60〜100%:文字を消す */
  100% { width: 0; }
}

ただし1種類のテキストをループさせるだけなら上記で十分ですが、複数のテキストを切り替えてループさせる場合は CSS だけでは困難です。後述の JavaScript 版を使いましょう。


複数行タイピングアニメーション

1行目が打ち終わったら2行目が打ち込まれる、という順番制御は animation-delay で実現します。

.line1,
.line2,
.line3 {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  display: block; /* 各行をブロック要素に */
}

/* 1行目:すぐ開始、14文字 */
.line1 {
  animation:
    typeLine1 1.4s steps(14, end) forwards,
    blink 0.75s step-end 3;
  border-right: 2px solid #333;
}

/* 2行目:1行目完了後に開始(1.5秒後)、18文字 */
.line2 {
  animation:
    typeLine2 1.8s steps(18, end) 1.6s forwards,
    blink 0.75s step-end 3;
  animation-delay: 1.6s, 1.6s;
  border-right: 2px solid #333;
}

/* 3行目:2行目完了後に開始(3.5秒後)、12文字 */
.line3 {
  animation:
    typeLine3 1.2s steps(12, end) 3.6s forwards,
    blink 0.75s step-end infinite;
  animation-delay: 3.6s, 3.6s;
  border-right: 2px solid #333;
}

@keyframes typeLine1 { to { width: 14ch; } }
@keyframes typeLine2 { to { width: 18ch; } }
@keyframes typeLine3 { to { width: 12ch; } }
<div class="terminal">
  <span class="line1">$ npm install</span>
  <span class="line2">Installing packages...</span>
  <span class="line3">Done! 12 added</span>
</div>

ターミナル風のスタイルと組み合わせる

.terminal {
  background: #1a1a2e;
  color: #00ff41;
  padding: 24px 28px;
  border-radius: 8px;
  font-family: 'Courier New', Courier, monospace;
  font-size: 1rem;
  line-height: 2;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.terminal::before {
  content: '';
  display: block;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #ff5f56;
  box-shadow: 20px 0 0 #ffbd2e, 40px 0 0 #27c93f;
  margin-bottom: 16px;
}

日本語タイピングアニメーションの注意点

日本語テキストに対してCSSタイピングアニメーションを使う場合、英数字のときとは異なる問題が発生します。

ch 単位は日本語では使えない

ch 単位は「0(ゼロ)の幅」を基準にするため、等幅フォントの英数字では「1文字 = 1ch」が成立します。しかし日本語の全角文字は通常 2ch 分の幅になるため、steps の数と幅が合いません。

/* 日本語:em 単位を使う方法 */
.typewriter-ja {
  font-family: monospace;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  /* 日本語10文字なら 10em(目安) */
  animation: typingJa 3s steps(10, end) forwards;
}

@keyframes typingJa {
  to {
    width: 10em; /* 文字数 × em で指定 */
  }
}

ただしem単位もフォントによって変わるため、完全に正確ではありません。最も確実なのは、実際にテキストを表示した状態の幅をブラウザの開発者ツールで測定し、px 単位で指定することです。

/* px 単位で確実に指定する方法 */
.typewriter-ja-px {
  font-size: 1.5rem;
  font-family: 'Hiragino Kaku Gothic Pro', 'Meiryo', sans-serif;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  /* 実際の幅を開発者ツールで計測して指定 */
  animation: typingJaPx 3s steps(10, end) forwards;
}

@keyframes typingJaPx {
  to {
    width: 240px; /* 実測値を入れる */
  }
}

日本語でプロポーショナルフォントを使う場合

プロポーショナルフォント(文字ごとに幅が異なるフォント)では、1文字ずつぴったり広がるタイピング効果はCSSだけでは実現できません。この場合は後述のJavaScript版を使います。


CSSの限界とJavaScript1行で解決する方法

CSSのみ と JS併用

CSSタイピングアニメーションには以下の限界があります。

機能 CSS JavaScript
1種類のテキストを表示 可能 可能
複数テキストを順番に切り替え 困難(長い@keyframesが必要) 簡単
プロポーショナルフォント対応 不可 可能
日本語を正確に1文字ずつ 困難 可能
動的なテキスト(APIから取得など) 不可 可能
ループ中のテキスト変更 不可 可能

JavaScript版タイピングアニメーション(シンプル実装)

// 1種類のテキストをタイピング表示
function typewriter(element, text, speed = 80) {
  let i = 0;
  element.textContent = '';
  const timer = setInterval(() => {
    if (i < text.length) {
      element.textContent += text.charAt(i);
      i++;
    } else {
      clearInterval(timer);
    }
  }, speed);
}

// 使い方
const el = document.querySelector('.typewriter-js');
typewriter(el, 'CSSタイピングアニメーション', 100);

複数テキストをループするJavaScript版

// 複数テキストを順番にタイピング→削除→次のテキスト
function multiTypewriter(element, texts, {
  typeSpeed = 80,
  deleteSpeed = 40,
  pauseAfterType = 1500,
  pauseAfterDelete = 400
} = {}) {
  let textIndex = 0;
  let charIndex = 0;
  let isDeleting = false;

  function tick() {
    const currentText = texts[textIndex];

    if (!isDeleting) {
      // タイピング中
      element.textContent = currentText.substring(0, charIndex + 1);
      charIndex++;

      if (charIndex === currentText.length) {
        // 打ち終わり → 待機してから削除開始
        isDeleting = true;
        setTimeout(tick, pauseAfterType);
        return;
      }
      setTimeout(tick, typeSpeed);
    } else {
      // 削除中
      element.textContent = currentText.substring(0, charIndex - 1);
      charIndex--;

      if (charIndex === 0) {
        // 削除完了 → 次のテキストへ
        isDeleting = false;
        textIndex = (textIndex + 1) % texts.length;
        setTimeout(tick, pauseAfterDelete);
        return;
      }
      setTimeout(tick, deleteSpeed);
    }
  }

  tick();
}

// 使い方
const el = document.querySelector('.multi-typewriter');
multiTypewriter(el, [
  'Webサイトを作ろう',
  'デザインを磨こう',
  'CSSを学ぼう'
]);
/* JS版のカーソルスタイル */
.multi-typewriter {
  display: inline-block;
  border-right: 2px solid currentColor;
  padding-right: 2px;
  animation: blink 0.75s step-end infinite;
  min-height: 1.5em; /* テキストが消えたときの高さ確保 */
}

@keyframes blink {
  0%, 100% { border-color: currentColor; }
  50%       { border-color: transparent; }
}

CSS @keyframesの基礎を理解した上でJavaScriptと組み合わせると、柔軟でメンテナンスしやすいアニメーションが書けます。


タイピングアニメーションのデザインバリエーション

グリーンターミナル風

.terminal-green {
  background: #0d1117;
  color: #39d353;
  font-family: 'Courier New', monospace;
  font-size: 1.1rem;
  padding: 20px 24px;
  border-radius: 6px;
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  border-right: 2px solid #39d353;
  animation:
    typing 2.5s steps(20, end) forwards,
    blink 0.6s step-end infinite;
}

@keyframes typing {
  to { width: 20ch; }
}

@keyframes blink {
  50% { border-color: transparent; }
}

ホワイト背景・ミニマル版

.typewriter-minimal {
  font-family: 'Georgia', serif;
  font-size: 2rem;
  color: #1a1a1a;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  border-right: 3px solid #d16176;
  padding-right: 4px;
  animation:
    typing 3s steps(16, end) forwards,
    blink 0.8s step-end infinite;
}

タイプライター後に下線フェードイン

.typewriter-underline {
  position: relative;
  display: inline-block;
  white-space: nowrap;
  overflow: hidden;
  width: 0;
  font-family: monospace;
  animation: typing 2s steps(14, end) forwards;
}

.typewriter-underline::after {
  content: '';
  position: absolute;
  bottom: -4px;
  left: 0;
  width: 100%;
  height: 3px;
  background: #d16176;
  transform: scaleX(0);
  transform-origin: left;
  animation: underlineIn 0.4s ease 2.1s forwards; /* タイピング完了後に発火 */
}

@keyframes underlineIn {
  to { transform: scaleX(1); }
}

よくある失敗と対処法

つまずき→対処

失敗1:steps の数と文字数が合わない

steps(N) の N はテキストの文字数に一致させる必要があります。ずれると、最後の数文字が一瞬で表示されたり、手前で止まったりします。

/* テキスト「Hello World」= 11文字(スペース含む) */

/* NG:文字数と steps が合っていない */
.bad { animation: typing 2s steps(10, end) forwards; }
/* NG:widthも合っていない */
@keyframes typing { to { width: 9ch; } } /* 10文字分しかない */

/* OK:文字数に合わせる */
.good { animation: typing 2s steps(11, end) forwards; }
@keyframes typing { to { width: 11ch; } }

失敗2:width: fit-content で使えない

width: fit-content は固定値ではないため、@keyframes の to で使うと動作しません。必ず固定の長さ(ch, em, px)で指定します。

失敗3:inline 要素でwidthが効かない

span など inline 要素では width が効きません。display: inline-block か display: block に変更する必要があります。

/* NG */
span.typewriter {
  width: 0; /* inline要素にwidthは効かない */
  overflow: hidden;
}

/* OK */
span.typewriter {
  display: inline-block; /* または block */
  width: 0;
  overflow: hidden;
}

アクセシビリティ:prefers-reduced-motion 対応

@media (prefers-reduced-motion: reduce) {
  .typewriter,
  .typewriter-cursor,
  .typewriter-loop {
    animation: none;
    width: auto;     /* テキストをすべて表示 */
    overflow: visible;
    border-right: none;
  }
}

reduced-motion 環境では width: 0 + overflow: hidden のままアニメーションを止めてしまうと、テキストが見えない状態のままになります。必ず width: auto と overflow: visible を設定してテキストが読める状態にしてください。


まとめ

タイピングアニメーションは、steps()でwidthを段階的に広げ、border-rightでカーソルを表現するのが基本です。ポイントは①文字数に合わせてwidthとsteps()の数を合わせる②等幅フォントで文字ズレを防ぐ③カーソル点滅は別の@keyframesで作る、の3点。複数行やJavaScript併用の応用も紹介したので、用途に合わせて選んでください。

よくある質問(FAQ)

CSSだけでタイピングアニメーションを作る場合、どんなフォントを使うべきですか?

等幅フォント(monospace)を使うことを推奨します。等幅フォントでは全文字が同じ幅なので「N文字 = Nch」が正確に成立し、steps() と width: Nch の計算が簡単になります。代表的な等幅フォントは Courier New、Consolas、’Source Code Pro’、’Fira Code’ などです。プロポーショナルフォント(ヒラギノ角ゴなど)でどうしてもタイピング風を実現したい場合は JavaScript 版を使ってください。

文字数が分からない動的テキストにCSSタイピングアニメーションを使えますか?

CSSのみでは困難です。steps() の数値と width の値をあらかじめ固定で書く必要があるため、動的なテキストへの対応はJavaScriptが必要です。JavaScriptでテキストの文字数を取得して steps と width を動的に設定するか、本記事で紹介したJavaScriptタイピング関数を使う方法を選んでください。

タイピングアニメーションを特定のページ要素が表示されたタイミングで開始したい

Intersection Observer API と組み合わせると、要素がビューポートに入ったタイミングでアニメーションを開始できます。初期状態ではアニメーションを animation: none に設定しておき、Intersection Observer のコールバックで is-visible などのクラスを追加してアニメーションを発火させるパターンが一般的です。

カーソルの色や形を変えることはできますか?

border-right のスタイルを変更することで色・太さを変えられます。border-right: 3px solid #d16176 のように書けばカーソルが太くなり色も変わります。ブロック状のカーソル(■)を作りたい場合は、::after 疑似要素でinline-blockの四角形を作り、blink アニメーションで opacity を 0/1 切り替える方法が使えます。

複数のテキストを自動で切り替えるライブラリはありますか?

Typed.js や TypeIt などの軽量ライブラリが有名です。ただし、記事で紹介したJavaScriptのmultiTypewriter関数程度の機能であればライブラリ不要で実装できます。外部依存を減らしたい場合は自前実装をおすすめします。パフォーマンス上も自前実装の方がバンドルサイズを抑えられます。


あわせて読みたい関連記事

CSS・Webデザイン実装の総まとめ

CSSの実装テクニックを「レイアウト・装飾・アニメーション・基礎・トラブル解決」の目的別に探せる総まとめページを用意しています。実装で迷ったときの索引としてどうぞ。

AIスキルで、未来の自分をアップデート!

今なら完全無料でAIを学べる!WithAI

今なら完全無料でAIを学べる!

  • 動画や実践で楽しく学べる:初心者でも安心のカリキュラム
  • スマホ・PCどちらでもOK:好きな時間に学習できる
  • 料金は一切ナシ0円でAIスキルが身につく

目的に合わせて選べる「AI副業」「AI転職」「AI活用」の3コースを用意。副収入・キャリアチェンジ・日常の生産性アップまで、あなたのゴールに合わせてAIを学べます。

会員登録はカンタン30秒で完了します。まずは公式LINEから、無料でAI学習をスタートしましょう!

この記事を書いた人

WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次