ドットローダー集
バウンド・チャット入力中・波の3パターンのドットアニメーションを純CSSで実装。軽量で省スペースな待機表現として汎用的に使えます。
ライブデモ
使用例(お題: アイドルグループ 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で提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。