ドットローダー集

バウンド・チャット入力中・波の3パターンのドットアニメーションを純CSSで実装。軽量で省スペースな待機表現として汎用的に使えます。

#css#animation#dots

ライブデモ

使用例(お題: アイドルグループ Sakura)

この技法を「アイドルグループ Sakura」というテーマのダミーサイトで実際に使った例です。

HTML
<!-- Sakura ライブ配信チャット:入力中ドット→メッセージ着信。下部に波ドットの読込 -->
<div class="sk-live">
  <header class="sk-live__bar">
    <span class="sk-live__name">🌸 Sakura LIVE</span>
    <span class="sk-live__on"><i></i>配信中</span>
  </header>

  <div class="sk-feed" id="skFeed">
    <div class="sk-msg sk-msg--in">
      <span class="sk-msg__av">ひ</span>
      <p class="sk-msg__bub">今日も来てくれてありがとう🌸</p>
    </div>
    <div class="sk-msg sk-msg--in">
      <span class="sk-msg__av">ひ</span>
      <p class="sk-msg__bub">次の楽曲、特別に少しだけ…</p>
    </div>

    <!-- 入力中インジケータ(ドットローダー) -->
    <div class="sk-typing" id="skTyping">
      <span class="sk-msg__av">ひ</span>
      <div class="sk-dots"><i></i><i></i><i></i></div>
    </div>
  </div>

  <!-- 下部:次のコメント読み込み(波ドット) -->
  <div class="sk-loadmore">
    <div class="sk-wave"><i></i><i></i><i></i><i></i><i></i></div>
    <span>新しいコメントを読み込み中</span>
  </div>
</div>
CSS
/* Sakura ライブチャット:ドットローダー(入力中+波) */
:root {
  --pink: #ffd1e0;
  --pink2: #ff8fb3;
  --gray: #f3f4f7;
  --ink: #4a4450;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  height: 400px;
  display: grid;
  place-items: center;
  font-family: "Hiragino Kaku Gothic ProN", system-ui, sans-serif;
  background: linear-gradient(180deg, #fff 0%, var(--pink) 100%);
  color: var(--ink);
  overflow: hidden;
}

.sk-live {
  width: 330px;
  height: 360px;
  display: flex;
  flex-direction: column;
  background: #fff;
  border-radius: 20px;
  box-shadow: 0 16px 40px rgba(255,143,179,0.3);
  overflow: hidden;
}

.sk-live__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 13px 16px;
  background: var(--pink);
  color: #7a3b53;
  font-weight: 700;
  font-size: 14px;
}
.sk-live__on { display: flex; align-items: center; gap: 6px; font-size: 11px; }
.sk-live__on i {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: #ff4d6d;
  animation: sk-blink 1.2s ease-in-out infinite;
}
@keyframes sk-blink { 50% { opacity: 0.3; } }

.sk-feed { flex: 1; padding: 14px; display: flex; flex-direction: column; gap: 10px; }

.sk-msg { display: flex; align-items: flex-end; gap: 8px; }
.sk-msg__av {
  flex: none;
  width: 28px; height: 28px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--pink2);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
}
.sk-msg__bub {
  margin: 0;
  padding: 9px 13px;
  border-radius: 4px 14px 14px 14px;
  background: var(--gray);
  font-size: 12.5px;
  line-height: 1.5;
  max-width: 220px;
}
.sk-msg--new .sk-msg__bub { background: var(--pink); animation: sk-pop 0.3s ease both; }
@keyframes sk-pop { from { opacity: 0; transform: translateY(6px); } }

/* 入力中ドット(バウンド) */
.sk-typing { display: flex; align-items: center; gap: 8px; }
.sk-dots {
  display: flex;
  gap: 5px;
  padding: 11px 14px;
  border-radius: 4px 14px 14px 14px;
  background: var(--gray);
}
.sk-dots i {
  width: 7px; height: 7px;
  border-radius: 50%;
  background: var(--pink2);
  animation: sk-bounce 1.2s ease-in-out infinite;
}
.sk-dots i:nth-child(2) { animation-delay: 0.18s; }
.sk-dots i:nth-child(3) { animation-delay: 0.36s; }
@keyframes sk-bounce { 0%, 60%, 100% { transform: translateY(0); } 30% { transform: translateY(-6px); } }

/* 下部:波ドット */
.sk-loadmore {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 11px 16px;
  border-top: 1px solid var(--gray);
  font-size: 11px;
  color: #9a8f96;
}
.sk-wave { display: flex; gap: 4px; align-items: center; height: 12px; }
.sk-wave i {
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--pink2);
  animation: sk-wave 1.1s ease-in-out infinite;
}
.sk-wave i:nth-child(2) { animation-delay: 0.1s; }
.sk-wave i:nth-child(3) { animation-delay: 0.2s; }
.sk-wave i:nth-child(4) { animation-delay: 0.3s; }
.sk-wave i:nth-child(5) { animation-delay: 0.4s; }
@keyframes sk-wave { 0%, 100% { transform: translateY(0); opacity: 0.5; } 50% { transform: translateY(-5px); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .sk-dots i, .sk-wave i, .sk-live__on i { animation: none; }
}
JavaScript
// Sakura ライブチャット:入力中ドット → メッセージ着信を繰り返す(ループ)
const feed = document.getElementById('skFeed');
const typing = document.getElementById('skTyping');

// 着信させるコメント候補
const lines = [
  'みんなのコール、最高だよ🌸',
  '新曲MV、明日0時公開だよ!',
  'ラスト、一緒に歌おうね🎤',
  'また会えてうれしい…!'
];
let i = 0;

function showMessage() {
  if (!feed || !typing) return;
  // 入力中ドットを隠してメッセージを差し込む
  typing.style.display = 'none';
  const msg = document.createElement('div');
  msg.className = 'sk-msg sk-msg--in sk-msg--new';
  msg.innerHTML = '<span class="sk-msg__av">ひ</span>' +
    '<p class="sk-msg__bub"></p>';
  const bub = msg.querySelector('.sk-msg__bub');
  if (bub) bub.textContent = lines[i % lines.length];
  feed.insertBefore(msg, typing);
  i++;

  // 古いメッセージを間引いて領域内に収める
  const msgs = feed.querySelectorAll('.sk-msg');
  if (msgs.length > 3) msgs[0].remove();

  // 少し置いて再び「入力中」へ
  setTimeout(() => {
    typing.style.display = 'flex';
    feed.appendChild(typing);
    setTimeout(showMessage, 1600);
  }, 1300);
}

// 初回:入力中を見せてから着信
setTimeout(showMessage, 1600);

コード

HTML
<!-- ドットローダー集: 3種のドットアニメーション(バウンド・タイピング・波) -->
<div class="dt-stage">
  <div class="dt-card">
    <!-- 1. 上下にバウンドする3点 -->
    <div class="dt-block">
      <div class="dt-bounce"><span></span><span></span><span></span></div>
      <p>Bounce</p>
    </div>
    <!-- 2. チャットの入力中インジケータ -->
    <div class="dt-block">
      <div class="dt-typing"><span></span><span></span><span></span></div>
      <p>Typing</p>
    </div>
    <!-- 3. 波のように拡大する5点 -->
    <div class="dt-block">
      <div class="dt-wave"><span></span><span></span><span></span><span></span><span></span></div>
      <p>Wave</p>
    </div>
  </div>
</div>
CSS
:root {
  --bg: #110e1d;
  --card: rgba(255, 255, 255, .04);
  --c1: #a78bfa;
  --c2: #22d3ee;
  --c3: #fb7185;
  --txt: #d8d4f0;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background:
    radial-gradient(700px 500px at 20% 10%, #241a47 0%, transparent 55%),
    radial-gradient(600px 500px at 90% 90%, #102a3a 0%, transparent 55%),
    var(--bg);
  color: var(--txt);
}
.dt-stage { padding: 26px; }
.dt-card {
  display: flex;
  gap: 18px;
  background: var(--card);
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 20px;
  padding: 26px 22px;
  backdrop-filter: blur(6px);
  box-shadow: 0 20px 50px rgba(0, 0, 0, .5);
}
.dt-block {
  display: grid;
  justify-items: center;
  gap: 16px;
  min-width: 110px;
}
.dt-block p {
  margin: 0;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(216, 212, 240, .5);
}
.dt-block > div { display: flex; align-items: center; gap: 8px; height: 28px; }

/* 1. バウンド */
.dt-bounce span {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--c1);
  animation: dt-bounce 1.2s ease-in-out infinite;
}
.dt-bounce span:nth-child(2) { animation-delay: .15s; background: var(--c2); }
.dt-bounce span:nth-child(3) { animation-delay: .3s; background: var(--c3); }

/* 2. タイピング */
.dt-typing {
  background: rgba(255, 255, 255, .06);
  border-radius: 999px;
  padding: 0 14px;
  gap: 6px !important;
}
.dt-typing span {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--c2);
  opacity: .4;
  animation: dt-fade 1.4s ease-in-out infinite;
}
.dt-typing span:nth-child(2) { animation-delay: .2s; }
.dt-typing span:nth-child(3) { animation-delay: .4s; }

/* 3. 波 */
.dt-wave span {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--c3);
  animation: dt-wave 1.1s ease-in-out infinite;
}
.dt-wave span:nth-child(1) { animation-delay: 0s; }
.dt-wave span:nth-child(2) { animation-delay: .1s; background: var(--c1); }
.dt-wave span:nth-child(3) { animation-delay: .2s; background: var(--c2); }
.dt-wave span:nth-child(4) { animation-delay: .3s; background: var(--c1); }
.dt-wave span:nth-child(5) { animation-delay: .4s; }

@keyframes dt-bounce { 0%, 80%, 100% { transform: translateY(0); opacity: .5; } 40% { transform: translateY(-14px); opacity: 1; } }
@keyframes dt-fade { 0%, 60%, 100% { opacity: .3; transform: scale(.85); } 30% { opacity: 1; transform: scale(1); } }
@keyframes dt-wave { 0%, 60%, 100% { transform: scale(.7); opacity: .5; } 30% { transform: scale(1.6); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .dt-bounce span, .dt-typing span, .dt-wave span { animation-duration: 3s; }
}
JavaScript
// このデモは純CSSアニメーションのみで動作するため JS は不要

🤖 AIエージェント用プロンプト

このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「ドットローダー集」の効果を追加してください。

# 追加してほしい効果
ドットローダー集(ローダー & スケルトン)
バウンド・チャット入力中・波の3パターンのドットアニメーションを純CSSで実装。軽量で省スペースな待機表現として汎用的に使えます。

# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】

# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- ドットローダー集: 3種のドットアニメーション(バウンド・タイピング・波) -->
<div class="dt-stage">
  <div class="dt-card">
    <!-- 1. 上下にバウンドする3点 -->
    <div class="dt-block">
      <div class="dt-bounce"><span></span><span></span><span></span></div>
      <p>Bounce</p>
    </div>
    <!-- 2. チャットの入力中インジケータ -->
    <div class="dt-block">
      <div class="dt-typing"><span></span><span></span><span></span></div>
      <p>Typing</p>
    </div>
    <!-- 3. 波のように拡大する5点 -->
    <div class="dt-block">
      <div class="dt-wave"><span></span><span></span><span></span><span></span><span></span></div>
      <p>Wave</p>
    </div>
  </div>
</div>

【CSS】
:root {
  --bg: #110e1d;
  --card: rgba(255, 255, 255, .04);
  --c1: #a78bfa;
  --c2: #22d3ee;
  --c3: #fb7185;
  --txt: #d8d4f0;
}
* { box-sizing: border-box; }
body {
  margin: 0;
  min-height: 100vh;
  display: grid;
  place-items: center;
  font-family: "Segoe UI", system-ui, sans-serif;
  background:
    radial-gradient(700px 500px at 20% 10%, #241a47 0%, transparent 55%),
    radial-gradient(600px 500px at 90% 90%, #102a3a 0%, transparent 55%),
    var(--bg);
  color: var(--txt);
}
.dt-stage { padding: 26px; }
.dt-card {
  display: flex;
  gap: 18px;
  background: var(--card);
  border: 1px solid rgba(255, 255, 255, .08);
  border-radius: 20px;
  padding: 26px 22px;
  backdrop-filter: blur(6px);
  box-shadow: 0 20px 50px rgba(0, 0, 0, .5);
}
.dt-block {
  display: grid;
  justify-items: center;
  gap: 16px;
  min-width: 110px;
}
.dt-block p {
  margin: 0;
  font-size: 11px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: rgba(216, 212, 240, .5);
}
.dt-block > div { display: flex; align-items: center; gap: 8px; height: 28px; }

/* 1. バウンド */
.dt-bounce span {
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--c1);
  animation: dt-bounce 1.2s ease-in-out infinite;
}
.dt-bounce span:nth-child(2) { animation-delay: .15s; background: var(--c2); }
.dt-bounce span:nth-child(3) { animation-delay: .3s; background: var(--c3); }

/* 2. タイピング */
.dt-typing {
  background: rgba(255, 255, 255, .06);
  border-radius: 999px;
  padding: 0 14px;
  gap: 6px !important;
}
.dt-typing span {
  width: 9px; height: 9px;
  border-radius: 50%;
  background: var(--c2);
  opacity: .4;
  animation: dt-fade 1.4s ease-in-out infinite;
}
.dt-typing span:nth-child(2) { animation-delay: .2s; }
.dt-typing span:nth-child(3) { animation-delay: .4s; }

/* 3. 波 */
.dt-wave span {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--c3);
  animation: dt-wave 1.1s ease-in-out infinite;
}
.dt-wave span:nth-child(1) { animation-delay: 0s; }
.dt-wave span:nth-child(2) { animation-delay: .1s; background: var(--c1); }
.dt-wave span:nth-child(3) { animation-delay: .2s; background: var(--c2); }
.dt-wave span:nth-child(4) { animation-delay: .3s; background: var(--c1); }
.dt-wave span:nth-child(5) { animation-delay: .4s; }

@keyframes dt-bounce { 0%, 80%, 100% { transform: translateY(0); opacity: .5; } 40% { transform: translateY(-14px); opacity: 1; } }
@keyframes dt-fade { 0%, 60%, 100% { opacity: .3; transform: scale(.85); } 30% { opacity: 1; transform: scale(1); } }
@keyframes dt-wave { 0%, 60%, 100% { transform: scale(.7); opacity: .5; } 30% { transform: scale(1.6); opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .dt-bounce span, .dt-typing span, .dt-wave span { animation-duration: 3s; }
}

【JavaScript】
// このデモは純CSSアニメーションのみで動作するため JS は不要

# 外部ライブラリ
なし(追加ライブラリ不要)

# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。