DEMO
CODE
<div class="demo-stage anl-stage"> <div class="anl-list" id="anl-list" aria-live="polite"></div> </div>
.anl-stage { display:flex; align-items:center; justify-content:center; padding:2rem; background:#0B0B10; min-height:280px; overflow:hidden; }
.anl-list { display:flex; flex-direction:column; gap:.7rem; width:100%; max-width:360px; }
.anl-item {
display:flex; align-items:center; gap:.9rem;
padding:.85rem 1rem; border-radius:12px;
background:#17171C; border:1px solid rgba(255,255,255,.08);
animation: anl-in .5s cubic-bezier(.22,1,.36,1) both;
}
.anl-icon {
flex:0 0 32px; width:32px; height:32px; border-radius:9px;
display:flex; align-items:center; justify-content:center; font-size:.95rem;
}
.anl-body { min-width:0; }
.anl-title { font-family:'Inter',sans-serif; font-size:.8rem; color:#EDEDED; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.anl-meta { font-family:'JetBrains Mono',monospace; font-size:.65rem; color:#6E6E7A; margin-top:.15rem; }
@keyframes anl-in {
from { opacity:0; transform:translateY(-14px) scale(.94); }
to { opacity:1; transform:translateY(0) scale(1); }
}
.anl-item.anl-out { animation: anl-out .4s ease forwards; }
@keyframes anl-out {
to { opacity:0; transform:scale(.94); margin-bottom:-3.2rem; }
}
@media (prefers-reduced-motion: reduce) {
.anl-item { animation-duration:.01ms; }
.anl-item.anl-out { animation-duration:.01ms; }
}
(function(){
var list = document.getElementById('anl-list');
if (!list) return;
var DATA = [
{ icon: '💸', bg: 'rgba(233,27,137,.16)', title: 'Payment received', meta: '$1,240.00' },
{ icon: '👤', bg: 'rgba(91,170,222,.16)', title: 'New user signed up', meta: 'from Osaka, JP' },
{ icon: '🚀', bg: 'rgba(123,55,168,.16)', title: 'Deploy succeeded', meta: 'build #1284' },
{ icon: '⭐', bg: 'rgba(255,193,7,.16)', title: 'Repository starred', meta: '+18 today' },
{ icon: '💬', bg: 'rgba(16,185,129,.16)', title: 'New message', meta: 'support inbox' }
];
var MAX = 4;
var i = 0;
function push(){
var d = DATA[i % DATA.length];
i++;
var el = document.createElement('div');
el.className = 'anl-item';
el.innerHTML = '<span class="anl-icon" style="background:' + d.bg + '">' + d.icon + '</span>' +
'<span class="anl-body"><span class="anl-title">' + d.title + '</span>' +
'<span class="anl-meta">' + d.meta + '</span></span>';
list.prepend(el);
while (list.children.length > MAX) {
var last = list.lastElementChild;
if (last.classList.contains('anl-out')) break;
last.classList.add('anl-out');
(function(node){ setTimeout(function(){ if (node.parentNode) node.parentNode.removeChild(node); }, 400); })(last);
}
}
push(); push(); push();
setInterval(push, 2200);
})();
AI PROMPT
新着アイテムが上から差し込まれ、古い行が下から抜けていく通知リストを作って。JSは要素を prepend するだけにし、動きはCSSに任せる。追加側は @keyframes で opacity: 0; transform: translateY(-14px) scale(.94) から opacity: 1; transform: none へ、animation-fill-mode: both。削除側は削除用クラスを付けて opacity: 0; transform: scale(.94); margin-bottom: 負の値(=行の高さぶん)へアニメーションさせ、アニメーション時間ぶん待ってから removeChild する。負の margin を使うのは、高さが縮む過程を再現して下の行が滑らかに詰まるようにするため。表示上限(例4件)を決め、超えたぶんから順に退場させる。要点: 追加のたびに全行を再描画すると重いので、既存要素には触らず新規要素だけを挿入する。読み上げ対応として親に aria-live="polite" を付ける。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
新着が上から差し込まれ、古い行が下から抜けていく通知リストの演出です。Magic UI の Animated List として知られ、SaaSのランディングページで「リアルタイムに動いているプロダクト」を示す装飾としてよく使われます。
当サイトの Toast Notification は単発の通知が一時的に現れて消える表現で、実際の操作フィードバックに使うものです。本ページは複数件が継続的に流れ続ける「状態のショーケース」という別テーマで、実データの表示にも装飾にも転用できます。
実装の勘所は退場処理です。要素を即座に削除すると下の行が飛ぶため、負の margin で高さの縮小を見せてから DOM を取り除きます。
行が詰まるときにガタつきます。どう直しますか?
退場する要素を即座に削除すると、下の行が瞬間的に上へ飛びます。削除用クラスで margin-bottom を行の高さぶんマイナスへアニメーションさせ、その完了を待ってから DOM から取り除くと、高さが縮む過程が見えて滑らかに詰まります。本ページのコードはこの方式です。
リストの上限は何件が適切ですか?
常時流れる通知の演出なら3〜5件が見やすい範囲です。上限を大きくすると、画面内で同時にアニメーションする要素が増えて負荷が上がるうえ、視線がどこを追えばよいか分からなくなります。履歴を全件見せたい場合は別ページに逃がし、この演出は「最新数件」に限定するのが定石です。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。