No.159
LOADING CSSJS

Typing Indicator

チャットで相手が入力中であることを示す、3つの点が順番に弾む吹き出し。入力中の表示から返信が現れるまでの流れを、CSSの animation-delay で作っています。

DEMO

こんにちは!

CODE

HTML
<div class="demo-stage tpi-stage">
  <div class="tpi-chat" id="tpi-chat" role="log" aria-live="polite" aria-label="チャットのデモ">
    <div class="tpi-msg tpi-me">こんにちは!</div>
  </div>
</div>
CSS
.tpi-stage { display:flex; align-items:center; justify-content:center; padding:1.5rem; background:#0B0B10; min-height:280px; }
.tpi-chat { width:100%; max-width:340px; min-height:210px; display:flex; flex-direction:column; justify-content:flex-end; gap:.5rem; }
.tpi-msg { max-width:75%; padding:.6rem .9rem; border-radius:16px; font-size:.86rem; line-height:1.5; animation: tpi-in .3s cubic-bezier(.22,1,.36,1) both; }
.tpi-me { align-self:flex-end; background:#E91B89; color:#fff; border-bottom-right-radius:4px; }
.tpi-you { align-self:flex-start; background:#1E1E26; color:#EDEDED; border-bottom-left-radius:4px; }
.tpi-typing { align-self:flex-start; display:flex; gap:5px; padding:.8rem .9rem; border-radius:16px; border-bottom-left-radius:4px; background:#1E1E26; animation: tpi-in .3s ease both; }
.tpi-typing i { width:7px; height:7px; border-radius:50%; background:#9A9AA6; animation: tpi-bounce 1.2s ease-in-out infinite; }
.tpi-typing i:nth-child(2) { animation-delay:.15s; }
.tpi-typing i:nth-child(3) { animation-delay:.3s; }
@keyframes tpi-bounce { 0%, 60%, 100% { transform:translateY(0); opacity:.5; } 30% { transform:translateY(-6px); opacity:1; } }
@keyframes tpi-in { from { opacity:0; transform:translateY(8px) scale(.96); } to { opacity:1; transform:none; } }
@media (prefers-reduced-motion: reduce) {
  .tpi-msg, .tpi-typing { animation:none; }
  .tpi-typing i { animation:none; opacity:.8; }
}
JS
(function(){
  var chat = document.getElementById('tpi-chat');
  if (!chat) return;
  var SCRIPT = [
    ['you', 'こんにちは!ご用件を伺います。'],
    ['me', '予約の変更はできますか?'],
    ['you', 'はい、変更できます。ご希望の日時を教えてください。']
  ];
  var step = 0;
  function add(who, text){
    var m = document.createElement('div');
    m.className = 'tpi-msg tpi-' + who; m.textContent = text;
    chat.appendChild(m);
    while (chat.children.length > 4) chat.removeChild(chat.firstElementChild);
  }
  function next(){
    if (step >= SCRIPT.length) {
      step = 0;
      setTimeout(function(){ chat.innerHTML = ''; add('me', 'こんにちは!'); setTimeout(next, 900); }, 2600);
      return;
    }
    var s = SCRIPT[step++];
    if (s[0] === 'you') {
      var t = document.createElement('div');
      t.className = 'tpi-typing'; t.setAttribute('aria-label', '相手が入力中');
      t.innerHTML = '<i></i><i></i><i></i>';
      chat.appendChild(t);
      setTimeout(function(){ chat.removeChild(t); add('you', s[1]); setTimeout(next, 1000); }, 1600);
    } else {
      add('me', s[1]); setTimeout(next, 900);
    }
  }
  setTimeout(next, 800);
})();

AI PROMPT

Claude Code Cursor v0
PROMPT
チャットで相手が入力中であることを示す Typing Indicator(3つの点が順番に弾む吹き出し)を作って。吹き出しの中に i 要素を3つ置き、@keyframes で 0%・60%・100% は translateY(0) で半透明、30% だけ translateY(-6px) で不透明にして1.2秒の無限ループにする。2つ目と3つ目に animation-delay を .15秒・.3秒ずつずらすと、波のように順番に弾む。デモでは会話の流れを再現する:相手の発言の前に入力中の吹き出しを1.6秒出し、それを取り除いてから返信を translateY(8px) scale(.96) → 元の位置 のアニメーションで表示する。自分の発言は右寄せでアクセント色、相手は左寄せで暗い色、発言者側の下の角だけ角丸を小さくする。表示する発言は最新4件までにして古いものから消す。チャット全体は role="log" aria-live="polite" にして新しい発言が読み上げられるようにし、入力中の吹き出しには aria-label="相手が入力中" を付ける。@media (prefers-reduced-motion: reduce) では点を弾ませずに静止表示する。

Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift

AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する

BOOKS · PR · AMAZON

📖 なかしまぁ先生のCSSアニメーション入門 📖 マイクロインタラクション ―UI/UXデザインの神が宿る細部 📖 Fundamentals of Web Animation with GSAP

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

学びを止めない · AMAZONサブスク

📚 Kindle Unlimited — 技術書・デザイン書が読み放題。30日無料体験 → 🎧 Audible — 移動中に耳で学ぶオーディオブック。30日無料体験 → 📦 Amazon Prime — Prime Reading・配送特典つき。30日無料体験 → 🎵 Music Unlimited — 作業用BGMに。1億曲が聴き放題・30日無料体験 →

※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。

RELATED EFFECTS

↗ MORE
→ DOTS PULSE → ANIMATED LIST → TOAST NOTIFICATION

Typing Indicator(入力中インジケーター)とは

チャットやメッセージアプリで、相手が今まさに文章を打っていることを示す表示です。3つの点が順番に弾む吹き出しは多くのチャットで使われている形で、「返事が来る」ことが分かるため、待っている間に画面を離れにくくなります。

当サイトの Dots Pulse は点が脈打つ汎用の読み込み表示、Animated List は通知が積み重なるリストです。本ページはチャットの吹き出しの中に置く入力中の表示と、返信が現れるまでの流れを扱う別テーマで、サポートチャットやAIチャットの画面に使えます。

AIの応答を待つ画面では、入力中の表示が「処理が止まっていない」ことを伝える役割も持ちます。新しい発言を読み上げに伝える role="log" と組み合わせると、画面を見ていない利用者にも会話の進行が伝わります。

よくある質問

点の動きを translateY だけで作る理由は?

transform の translateY はレイアウトを再計算せずに位置だけを動かせるため、多数の吹き出しがある画面でも軽く動きます。margin や top を動かすと、点の上下にある要素まで毎フレーム位置が計算し直されます。不透明度の変化も合わせると、弾んでいる点がより目立ちます。

入力中の表示はどのくらいの時間出すのが自然ですか?

実際のチャットでは、相手の入力状況をサーバーから受け取って表示と非表示を切り替えます。自動応答やデモで演出として使う場合は、返信の長さに応じて1〜2秒程度にすると自然に見えます。短すぎると表示された瞬間に消えて気づかれず、長すぎると待たされている印象になります。

コードは無料で使えますか?

はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。