01Gói giá hiện dần theo scroll
Khi bảng giá cuộn vào khung nhìn, từng thẻ gói trồi lên theo thứ tự, dẫn khách xem lần lượt từng thẻ.
Dùng choLanding page giới thiệu gói giá, section so sánh giá kiểu cuộn
IntersectionObservertransition-delayJS 7줄

<main class="stage">
<div class="sr-grid is-demo">
<div class="sr-card">
<span class="sr-card__tag">Basic</span>
<span class="sr-card__price">$9</span>
</div>
<div class="sr-card sr-card--mid">
<span class="sr-card__tag">Pro</span>
<span class="sr-card__price">$29</span>
</div>
<div class="sr-card">
<span class="sr-card__tag">Team</span>
<span class="sr-card__price">$79</span>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-blue;
@include stage($stage-ink);
.sr-grid {
width: min(340px, 90vw);
aspect-ratio: 340 / 190;
display: flex;
align-items: flex-end;
gap: $sp-2;
}
.sr-card {
flex: 1 1 0;
height: 78%;
border-radius: $r-sm;
box-sizing: border-box;
padding: $sp-3;
display: flex;
flex-direction: column;
justify-content: flex-end;
gap: $sp-1;
color: $subject-cream;
background: rgba(255, 255, 255, .08);
font: 600 12px/1 -apple-system, "Segoe UI", sans-serif;
}
.sr-card--mid {
height: 100%;
background: $subject-blue-deep; // 크림 글자가 얹히므로 진한 파랑
}
.sr-card__tag { opacity: 1; font-size: 11px; } // AA: 진한 파랑 면 위 크림 100% — 5.98:1
.sr-card__price { font-size: 20px; font-weight: 700; margin-top: $sp-1; }
// 실사용: IntersectionObserver 가 .sr-grid 에 .in 을 붙이면 카드가 순서대로 떠오른다
.sr-card {
opacity: 0;
transform: translateY($sp-4);
transition: opacity 400ms $easing, transform 400ms $easing;
}
.sr-grid.in .sr-card { opacity: 1; transform: none; }
.sr-grid.in .sr-card:nth-child(2) { transition-delay: 140ms; }
.sr-grid.in .sr-card:nth-child(3) { transition-delay: 280ms; }
// 데모 루프: is-demo 는 실사용 경로(.in, 실제 스크롤 진입)와 별개로 keyframes 로 반복 재생 (캡처용)
@keyframes sr-rise {
0% { opacity: 0; transform: translateY($sp-4); }
20%, 85% { opacity: 1; transform: none; }
100% { opacity: 0; transform: translateY($sp-4); }
}
.sr-grid.is-demo .sr-card { animation: sr-rise $duration $easing infinite; }
.sr-grid.is-demo .sr-card:nth-child(2) { animation-delay: 160ms; }
.sr-grid.is-demo .sr-card:nth-child(3) { animation-delay: 320ms; }
@media (prefers-reduced-motion: reduce) {
.sr-card,
.sr-grid.is-demo .sr-card {
animation: none !important;
transition: none !important;
opacity: 1 !important;
transform: none !important;
}
}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);
}
.sr-grid {
width: min(340px, 90vw);
aspect-ratio: 340/190;
display: flex;
align-items: flex-end;
gap: 8px;
}
.sr-card {
flex: 1 1 0;
height: 78%;
border-radius: 8px;
box-sizing: border-box;
padding: 12px;
display: flex;
flex-direction: column;
justify-content: flex-end;
gap: 4px;
color: #fff7e6;
background: rgba(255, 255, 255, 0.08);
font: 600 12px/1 -apple-system, "Segoe UI", sans-serif;
}
.sr-card--mid {
height: 100%;
background: #0a4fe8;
}
.sr-card__tag {
opacity: 1;
font-size: 11px;
}
.sr-card__price {
font-size: 20px;
font-weight: 700;
margin-top: 4px;
}
.sr-card {
opacity: 0;
transform: translateY(16px);
transition: opacity 400ms ease-out, transform 400ms ease-out;
}
.sr-grid.in .sr-card {
opacity: 1;
transform: none;
}
.sr-grid.in .sr-card:nth-child(2) {
transition-delay: 140ms;
}
.sr-grid.in .sr-card:nth-child(3) {
transition-delay: 280ms;
}
@keyframes sr-rise {
0% {
opacity: 0;
transform: translateY(16px);
}
20%, 85% {
opacity: 1;
transform: none;
}
100% {
opacity: 0;
transform: translateY(16px);
}
}
.sr-grid.is-demo .sr-card {
animation: sr-rise 2s ease-out infinite;
}
.sr-grid.is-demo .sr-card:nth-child(2) {
animation-delay: 160ms;
}
.sr-grid.is-demo .sr-card:nth-child(3) {
animation-delay: 320ms;
}
@media (prefers-reduced-motion: reduce) {
.sr-card,
.sr-grid.is-demo .sr-card {
animation: none !important;
transition: none !important;
opacity: 1 !important;
transform: none !important;
}
}var grid = document.querySelector('.sr-grid');
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) { grid.classList.add('in'); io.unobserve(grid); }
});
}, { threshold: 0.3 });
io.observe(grid);import { CSSProperties, useEffect, useRef, useState } from "react";
import styles from "./PlansRevealOnScroll.module.scss";
export interface PlansRevealOnScrollProps {
duration?: string;
easing?: string;
stagger?: string;
}
const PLANS = [
{ tag: "Basic", price: "$9" },
{ tag: "Pro", price: "$29" },
{ tag: "Team", price: "$79" },
];
// 실사용: 진짜 IntersectionObserver 가 그리드가 뷰포트에 들어올 때 in 을 켠다. 카드마다 transition-delay 만 다르다.
export default function PlansRevealOnScroll({
duration = "400ms",
easing = "ease-out",
stagger = "140ms",
}: PlansRevealOnScrollProps) {
const gridRef = useRef<HTMLDivElement>(null);
const [inView, setInView] = useState(false);
useEffect(() => {
const el = gridRef.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => { if (e.isIntersecting) { setInView(true); io.unobserve(e.target); } });
}, { threshold: 0.3 });
io.observe(el);
return () => io.disconnect();
}, []);
const style = { "--duration": duration, "--easing": easing } as CSSProperties;
return (
<div className={styles.grid} style={style} ref={gridRef}>
{PLANS.map((p, i) => (
<div
key={p.tag}
className={`${styles.card} ${i === 1 ? styles.mid : ""} ${inView ? styles.in : ""}`}
style={{ transitionDelay: inView ? `calc(${stagger} * ${i})` : undefined }}
>
<span className={styles.tag}>{p.tag}</span>
<span className={styles.price}>{p.price}</span>
</div>
))}
</div>
);
}01 Gói giá hiện dần theo scroll — Diện tích đo được 37.606% · khung hình chuyển động 12/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 mẫu css pricing table animation copy-paste
Bài viết chứa demo này
9 mẫu css pricing table animation copy-pasteCss pricing table là khối hiển thị các gói giá xếp cạnh nhau trên một trang. Chín animation cho bảng giá dưới đây viết g Xem tất cả demo trong bài này


