01Cascade danh sách
Từng dòng trong danh sách trồi lên từ phía dưới, hơi thu nhỏ rồi bật vào đúng chỗ từng cái một — có chiều sâu hơn hẳn một fade đơn thuần.
Dùng choFeed thông báo, danh sách kết quả tìm kiếm
scale+translateY90ms 간격ease-pop

<main class="stage">
<ul class="cascade is-demo">
<li>Tin nhắn mới</li>
<li>Thanh toán xong</li>
<li>Tích điểm</li>
</ul>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-base; // 항목 하나가 나타나는 시간
$easing: $ease-pop; // 톡 튀어 올라오는 느낌
$stagger: 90ms; // 항목 사이 시차
@include stage($stage-ink);
.cascade {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
max-width: min(400px, 90vw);
font-weight: 600; font-size: clamp(12px, 3.4vw, 15px); line-height: 1.2; font-family: inherit;
li {
padding: clamp(8px, 2.6vw, 11px) clamp(12px, 3.4vw, 16px);
border-radius: $r-pill;
background: $subject-cream;
color: $stage-ink;
box-shadow: $shadow-raised;
animation: cascade-loop $dur-loop $easing infinite;
white-space: nowrap;
}
@for $i from 1 through 3 {
li:nth-child(#{$i}) { animation-delay: ($i - 1) * $stagger; }
}
}
@keyframes cascade-loop {
0%, 8% { opacity: 0; transform: translateY(14px) scale(.9); }
26%, 66% { opacity: 1; transform: translateY(0) scale(1); }
86%, 100% { opacity: 0; transform: translateY(-8px) scale(.96); }
}
@media (prefers-reduced-motion: reduce) {
.cascade li { animation: none; opacity: 1; transform: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.cascade {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px;
max-width: min(400px, 90vw);
font-weight: 600;
font-size: clamp(12px, 3.4vw, 15px);
line-height: 1.2;
font-family: inherit;
}
.cascade li {
padding: clamp(8px, 2.6vw, 11px) clamp(12px, 3.4vw, 16px);
border-radius: 999px;
background: #fff7e6;
color: #17141a;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
animation: cascade-loop 2s cubic-bezier(0.2, 1.4, 0.4, 1) infinite;
white-space: nowrap;
}
.cascade li:nth-child(1) {
animation-delay: 0ms;
}
.cascade li:nth-child(2) {
animation-delay: 90ms;
}
.cascade li:nth-child(3) {
animation-delay: 180ms;
}
@keyframes cascade-loop {
0%, 8% {
opacity: 0;
transform: translateY(14px) scale(0.9);
}
26%, 66% {
opacity: 1;
transform: translateY(0) scale(1);
}
86%, 100% {
opacity: 0;
transform: translateY(-8px) scale(0.96);
}
}
@media (prefers-reduced-motion: reduce) {
.cascade li {
animation: none;
opacity: 1;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./ListCascade.module.scss";
export type ListCascadeProps = {
items: string[];
/** 항목 하나가 나타나는 시간 (기본 300ms) */
duration?: number;
easing?: string;
/** 항목 사이 시차 ms (기본 90ms) */
stagger?: number;
};
export default function ListCascade({
items,
duration = 300,
easing = "cubic-bezier(.2,1.4,.4,1)",
stagger = 90,
}: ListCascadeProps) {
return (
<ul className={styles.cascade} style={{ "--duration": `${duration}ms`, "--easing": easing } as CSSProperties}>
{items.map((label, i) => (
<li key={label} style={{ animationDelay: `${i * stagger}ms` }}>
{label}
</li>
))}
</ul>
);
}01 Cascade danh sách — Diện tích đo được 9.301% · khung hình chuyển động 12/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 CSS Stagger Animation Demos Copy-Paste Là Chạy
Bài viết chứa demo này
9 CSS Stagger Animation Demos Copy-Paste Là ChạyCSS stagger animation là kỹ thuật khiến các phần tử trong danh sách hay lưới xuất hiện trễ nhau, thay vì bung ra cùng lú Xem tất cả demo trong bài này


