完了・受信箱ゼロ
チェックマークが描かれ、紙吹雪が漂う「すべて完了」の空状態。ボタンを押すたびに紙吹雪が弾け、達成感を演出します。受信箱ゼロの表示に最適です。
ライブデモ
コード
HTML
<!-- 空状態: 完了・受信箱ゼロ。チェックが描画され、紙吹雪が漂う。CTAクリックで紙吹雪がもう一度弾ける -->
<main class="ead-card">
<svg class="ead-illus" viewBox="0 0 200 170" width="168" height="143" role="img" aria-label="すべてのタスクが完了したことを示す、チェックマークと紙吹雪のイラスト">
<g class="ead-confetti" data-confetti aria-hidden="true">
<rect class="ead-piece ead-piece--1" x="28" y="38" width="10" height="10" rx="2"></rect>
<circle class="ead-piece ead-piece--2" cx="170" cy="44" r="6"></circle>
<rect class="ead-piece ead-piece--3" x="152" y="120" width="9" height="9" rx="2"></rect>
<circle class="ead-piece ead-piece--4" cx="32" cy="122" r="5"></circle>
<rect class="ead-piece ead-piece--5" x="96" y="16" width="8" height="8" rx="2"></rect>
<circle class="ead-piece ead-piece--6" cx="158" cy="86" r="4.5"></circle>
</g>
<g class="ead-badge-group">
<circle class="ead-badge-ring" cx="100" cy="82" r="40"></circle>
<path class="ead-check" d="M82 82 L94 94 L120 66"></path>
</g>
</svg>
<h2 class="ead-title">すべて完了しました!</h2>
<p class="ead-desc">新しい通知はありません。今のうちに、少し休憩してくださいね。</p>
<button class="ead-cta" type="button" data-cta>受信箱に戻る</button>
</main>
CSS
/* 空状態: 完了・受信箱ゼロ */
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 32px 20px;
font-family: "Hiragino Sans", "Yu Gothic UI", "Segoe UI", system-ui, -apple-system, sans-serif;
background:
radial-gradient(circle at 18% 16%, rgba(52, 211, 153, .16) 0%, transparent 46%),
radial-gradient(circle at 86% 84%, rgba(253, 224, 71, .2) 0%, transparent 50%),
#f7fdf9;
color: #065f46;
}
.ead-card {
width: 100%;
max-width: 380px;
background: #ffffff;
border-radius: 24px;
padding: 40px 32px 34px;
text-align: center;
box-shadow: 0 24px 60px -24px rgba(5, 150, 105, .24), 0 2px 8px rgba(6, 95, 70, .06);
border: 1px solid rgba(52, 211, 153, .14);
}
.ead-illus { display: block; margin: 0 auto 8px; overflow: visible; }
.ead-badge-group {
transform-box: fill-box;
transform-origin: center;
animation: ead-breathe 3s ease-in-out infinite;
}
@keyframes ead-breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.04); }
}
.ead-badge-ring { fill: #d1fae5; stroke: #10b981; stroke-width: 4; }
.ead-check {
fill: none;
stroke: #047857;
stroke-width: 8;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 56;
stroke-dashoffset: 56;
animation: ead-draw .6s .15s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes ead-draw { to { stroke-dashoffset: 0; } }
/* 漂う紙吹雪(アイドル) */
.ead-piece {
transform-box: fill-box;
transform-origin: center;
animation: ead-float 3.6s ease-in-out infinite;
}
.ead-piece--1 { fill: #fbbf24; animation-delay: 0s; }
.ead-piece--2 { fill: #60a5fa; animation-delay: .5s; }
.ead-piece--3 { fill: #f472b6; animation-delay: 1s; }
.ead-piece--4 { fill: #a78bfa; animation-delay: 1.4s; }
.ead-piece--5 { fill: #34d399; animation-delay: .8s; }
.ead-piece--6 { fill: #fb923c; animation-delay: 1.8s; }
@keyframes ead-float {
0%, 100% { transform: translateY(0) rotate(0deg); opacity: .55; }
50% { transform: translateY(-8px) rotate(50deg); opacity: 1; }
}
/* クリックで再生する紙吹雪バースト */
.ead-piece.is-bursting { animation: ead-burst .7s cubic-bezier(.22,1,.36,1); }
@keyframes ead-burst {
0% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 1; }
70% { opacity: 1; }
100% { transform: translate(var(--bx, 10px), var(--by, -18px)) scale(.6) rotate(160deg); opacity: 0; }
}
.ead-piece--1.is-bursting { --bx: -18px; --by: -16px; }
.ead-piece--2.is-bursting { --bx: 20px; --by: -14px; }
.ead-piece--3.is-bursting { --bx: 16px; --by: 18px; }
.ead-piece--4.is-bursting { --bx: -20px; --by: 16px; }
.ead-piece--5.is-bursting { --bx: 6px; --by: -22px; }
.ead-piece--6.is-bursting { --bx: 18px; --by: -6px; }
.ead-title { margin: 6px 0 10px; font-size: 21px; font-weight: 700; color: #065f46; }
.ead-desc { margin: 0 0 24px; font-size: 14.5px; line-height: 1.7; color: #4d7c6f; }
.ead-cta {
appearance: none;
border: none;
cursor: pointer;
padding: 13px 30px;
font-size: 15px;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #34d399 0%, #059669 100%);
border-radius: 999px;
box-shadow: 0 12px 26px -10px rgba(5, 150, 105, .5);
transition: transform .25s cubic-bezier(.22,1,.36,1), box-shadow .25s ease;
}
.ead-cta:hover { transform: translateY(-2px); box-shadow: 0 16px 32px -10px rgba(5, 150, 105, .6); }
.ead-cta:active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
.ead-badge-group, .ead-piece { animation: none !important; }
.ead-check { stroke-dashoffset: 0; animation: none !important; }
}
JavaScript
// 空状態: 完了・受信箱ゼロ — CTAクリックで紙吹雪をもう一度弾けさせる(お祝いの再生ボタン的な演出)
(() => {
const cta = document.querySelector('[data-cta]');
const pieces = document.querySelectorAll('.ead-piece');
if (!cta || !pieces || pieces.length === 0) return; // null安全
cta.addEventListener('click', () => {
pieces.forEach((piece) => {
piece.classList.remove('is-bursting');
// 強制リフローで同じアニメーションを再始動できるようにする
void piece.getBoundingClientRect();
piece.classList.add('is-bursting');
});
});
pieces.forEach((piece) => {
piece.addEventListener('animationend', (e) => {
if (e.animationName === 'ead-burst') piece.classList.remove('is-bursting');
});
});
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「完了・受信箱ゼロ」の効果を追加してください。
# 追加してほしい効果
完了・受信箱ゼロ(アラート & 空状態)
チェックマークが描かれ、紙吹雪が漂う「すべて完了」の空状態。ボタンを押すたびに紙吹雪が弾け、達成感を演出します。受信箱ゼロの表示に最適です。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 空状態: 完了・受信箱ゼロ。チェックが描画され、紙吹雪が漂う。CTAクリックで紙吹雪がもう一度弾ける -->
<main class="ead-card">
<svg class="ead-illus" viewBox="0 0 200 170" width="168" height="143" role="img" aria-label="すべてのタスクが完了したことを示す、チェックマークと紙吹雪のイラスト">
<g class="ead-confetti" data-confetti aria-hidden="true">
<rect class="ead-piece ead-piece--1" x="28" y="38" width="10" height="10" rx="2"></rect>
<circle class="ead-piece ead-piece--2" cx="170" cy="44" r="6"></circle>
<rect class="ead-piece ead-piece--3" x="152" y="120" width="9" height="9" rx="2"></rect>
<circle class="ead-piece ead-piece--4" cx="32" cy="122" r="5"></circle>
<rect class="ead-piece ead-piece--5" x="96" y="16" width="8" height="8" rx="2"></rect>
<circle class="ead-piece ead-piece--6" cx="158" cy="86" r="4.5"></circle>
</g>
<g class="ead-badge-group">
<circle class="ead-badge-ring" cx="100" cy="82" r="40"></circle>
<path class="ead-check" d="M82 82 L94 94 L120 66"></path>
</g>
</svg>
<h2 class="ead-title">すべて完了しました!</h2>
<p class="ead-desc">新しい通知はありません。今のうちに、少し休憩してくださいね。</p>
<button class="ead-cta" type="button" data-cta>受信箱に戻る</button>
</main>
【CSS】
/* 空状態: 完了・受信箱ゼロ */
* { box-sizing: border-box; }
body {
margin: 0;
min-height: 100vh;
display: grid;
place-items: center;
padding: 32px 20px;
font-family: "Hiragino Sans", "Yu Gothic UI", "Segoe UI", system-ui, -apple-system, sans-serif;
background:
radial-gradient(circle at 18% 16%, rgba(52, 211, 153, .16) 0%, transparent 46%),
radial-gradient(circle at 86% 84%, rgba(253, 224, 71, .2) 0%, transparent 50%),
#f7fdf9;
color: #065f46;
}
.ead-card {
width: 100%;
max-width: 380px;
background: #ffffff;
border-radius: 24px;
padding: 40px 32px 34px;
text-align: center;
box-shadow: 0 24px 60px -24px rgba(5, 150, 105, .24), 0 2px 8px rgba(6, 95, 70, .06);
border: 1px solid rgba(52, 211, 153, .14);
}
.ead-illus { display: block; margin: 0 auto 8px; overflow: visible; }
.ead-badge-group {
transform-box: fill-box;
transform-origin: center;
animation: ead-breathe 3s ease-in-out infinite;
}
@keyframes ead-breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.04); }
}
.ead-badge-ring { fill: #d1fae5; stroke: #10b981; stroke-width: 4; }
.ead-check {
fill: none;
stroke: #047857;
stroke-width: 8;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 56;
stroke-dashoffset: 56;
animation: ead-draw .6s .15s cubic-bezier(.22,1,.36,1) forwards;
}
@keyframes ead-draw { to { stroke-dashoffset: 0; } }
/* 漂う紙吹雪(アイドル) */
.ead-piece {
transform-box: fill-box;
transform-origin: center;
animation: ead-float 3.6s ease-in-out infinite;
}
.ead-piece--1 { fill: #fbbf24; animation-delay: 0s; }
.ead-piece--2 { fill: #60a5fa; animation-delay: .5s; }
.ead-piece--3 { fill: #f472b6; animation-delay: 1s; }
.ead-piece--4 { fill: #a78bfa; animation-delay: 1.4s; }
.ead-piece--5 { fill: #34d399; animation-delay: .8s; }
.ead-piece--6 { fill: #fb923c; animation-delay: 1.8s; }
@keyframes ead-float {
0%, 100% { transform: translateY(0) rotate(0deg); opacity: .55; }
50% { transform: translateY(-8px) rotate(50deg); opacity: 1; }
}
/* クリックで再生する紙吹雪バースト */
.ead-piece.is-bursting { animation: ead-burst .7s cubic-bezier(.22,1,.36,1); }
@keyframes ead-burst {
0% { transform: translate(0, 0) scale(1) rotate(0deg); opacity: 1; }
70% { opacity: 1; }
100% { transform: translate(var(--bx, 10px), var(--by, -18px)) scale(.6) rotate(160deg); opacity: 0; }
}
.ead-piece--1.is-bursting { --bx: -18px; --by: -16px; }
.ead-piece--2.is-bursting { --bx: 20px; --by: -14px; }
.ead-piece--3.is-bursting { --bx: 16px; --by: 18px; }
.ead-piece--4.is-bursting { --bx: -20px; --by: 16px; }
.ead-piece--5.is-bursting { --bx: 6px; --by: -22px; }
.ead-piece--6.is-bursting { --bx: 18px; --by: -6px; }
.ead-title { margin: 6px 0 10px; font-size: 21px; font-weight: 700; color: #065f46; }
.ead-desc { margin: 0 0 24px; font-size: 14.5px; line-height: 1.7; color: #4d7c6f; }
.ead-cta {
appearance: none;
border: none;
cursor: pointer;
padding: 13px 30px;
font-size: 15px;
font-weight: 600;
color: #fff;
background: linear-gradient(135deg, #34d399 0%, #059669 100%);
border-radius: 999px;
box-shadow: 0 12px 26px -10px rgba(5, 150, 105, .5);
transition: transform .25s cubic-bezier(.22,1,.36,1), box-shadow .25s ease;
}
.ead-cta:hover { transform: translateY(-2px); box-shadow: 0 16px 32px -10px rgba(5, 150, 105, .6); }
.ead-cta:active { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
.ead-badge-group, .ead-piece { animation: none !important; }
.ead-check { stroke-dashoffset: 0; animation: none !important; }
}
【JavaScript】
// 空状態: 完了・受信箱ゼロ — CTAクリックで紙吹雪をもう一度弾けさせる(お祝いの再生ボタン的な演出)
(() => {
const cta = document.querySelector('[data-cta]');
const pieces = document.querySelectorAll('.ead-piece');
if (!cta || !pieces || pieces.length === 0) return; // null安全
cta.addEventListener('click', () => {
pieces.forEach((piece) => {
piece.classList.remove('is-bursting');
// 強制リフローで同じアニメーションを再始動できるようにする
void piece.getBoundingClientRect();
piece.classList.add('is-bursting');
});
});
pieces.forEach((piece) => {
piece.addEventListener('animationend', (e) => {
if (e.animationName === 'ead-burst') piece.classList.remove('is-bursting');
});
});
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。