Card Stack Scroll
滾動時卡片依序吸頂堆疊、逐層縮小退場的滾動效果。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add card-stack-scrollCard Stack Scroll 是用 React、CSS position: sticky 與 Motion 的 useScroll 做的捲動堆疊卡片(sticky stacking cards)元件:往下捲時每張卡片依序吸頂、疊在前一張上面,被蓋住的卡片會逐漸縮小退到後方。
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<CardStackScroll/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
適用情境
- 產品首頁依序介紹三到五個核心功能,每張卡片一個重點
- 作品集或案例研究頁,一張卡片一個專案
- 「如何運作」的步驟說明,讓讀者一步一步往下捲
- 方案比較前的使用情境鋪陳
- 不適合:希望使用者左右滑動切換、不綁定頁面捲動時,改用 Stacked Peek Carousel
安裝
npx webberui@latest add https://webberui.com/r/card-stack-scroll.json使用
每個直接子元素會成為一張堆疊卡片:
import { CardStackScroll } from "@/components/ui/card-stack-scroll";
<CardStackScroll offsetTop={96}>
<div className="h-80 rounded-xl border bg-white p-8">第一張</div>
<div className="h-80 rounded-xl border bg-white p-8">第二張</div>
<div className="h-80 rounded-xl border bg-white p-8">第三張</div>
</CardStackScroll>範例
配合固定導覽列
頁面有 64px 高的固定 header 時,把 offsetTop 設得比 header 高一些,卡片才不會吸頂在導覽列底下。卡片會互相重疊,記得給不透明的背景色。
import { CardStackScroll } from "@/components/ui/card-stack-scroll";
const features = [
{ title: "自動備份", body: "每小時備份一次,保留 30 天。" },
{ title: "團隊權限", body: "依角色設定檢視、編輯與管理權限。" },
{ title: "稽核紀錄", body: "所有操作都有時間與操作者紀錄。" },
];
export function FeatureStack() {
return (
<CardStackScroll
offsetTop={88}
peek={16}
cardClassName="h-[420px] rounded-2xl border bg-white p-10 shadow-lg dark:bg-neutral-900"
>
{features.map((f) => (
<article key={f.title}>
<h3 className="text-2xl font-semibold">{f.title}</h3>
<p className="mt-3 text-neutral-600 dark:text-neutral-400">{f.body}</p>
</article>
))}
</CardStackScroll>
);
}cardClassName 會套用到每張卡片的動畫層,統一寫外觀比在每個子元素上重複 class 省事。
調整堆疊的層次感
peek 決定後方卡片露出多少,scaleStep 決定每層縮小多少;最後一張卡片不縮小,越前面的卡片縮得越多。
<CardStackScroll peek={24} scaleStep={0.06}>
<div className="h-96 rounded-xl bg-indigo-600 p-8 text-white">第一步:連結資料來源</div>
<div className="h-96 rounded-xl bg-indigo-500 p-8 text-white">第二步:設定同步排程</div>
<div className="h-96 rounded-xl bg-indigo-400 p-8 text-white">第三步:發布儀表板</div>
</CardStackScroll>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | ReactNode | — | 每個直接子元素成為一張卡片 |
offsetTop | number | 96 | 卡片吸頂位置(px) |
peek | number | 12 | 堆疊時每層向下錯開的距離(px) |
scaleStep | number | 0.04 | 疊入時每層縮小的比例 |
container | RefObject<HTMLElement> | — | 滾動發生在巢狀 overflow 容器內時,傳入該容器的 ref |
cardClassName | string | — | 套用到每張卡片外層 |
細節
- 以
useScroll+useTransform驅動,動畫跑在 transform 上,不觸發 layout - 使用者系統開啟「減少動態效果」時,退化為一般垂直列表
- 容器可以是頁面本身,也可以是任何
overflow-y: auto的區塊(如上方示範)
常見問題
卡片沒有吸頂,直接捲走了?
元件靠 position: sticky 定位。如果卡片和捲動容器之間的祖先元素設了 overflow: hidden 或 overflow: auto,sticky 會失效。在巢狀捲動區塊裡使用時,要把那個捲動容器的 ref 傳給 container。
可以在 Next.js App Router 使用嗎?
可以。原始碼已經有 "use client",子元素可以是 Server Component 渲染出來的內容,直接當 children 傳入即可。「減少動態效果」的靜態版在掛載後才切換,首次渲染與伺服器輸出一致。
效能如何?
縮放只改 transform(卡片帶有 will-change-transform),不觸發版面重排;捲動進度由 Motion 的 useScroll 提供,元件本身沒有額外的 scroll 事件監聽。
開啟「減少動態效果」時會怎樣?
卡片改為一般的垂直列表,間距 gap-6,沒有吸頂與縮放。
需要哪些依賴?
motion、clsx、tailwind-merge,以及 @/lib/utils 的 cn。不用 CLI 時,可以從「手動」分頁複製原始碼。