滾動效果

Card Stack Scroll

滾動時卡片依序吸頂堆疊、逐層縮小退場的滾動效果。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add card-stack-scroll

還沒有專案,或安裝遇到問題?看快速開始 →

Card Stack Scroll 是用 React、CSS position: sticky 與 Motion 的 useScroll 做的捲動堆疊卡片(sticky stacking cards)元件:往下捲時每張卡片依序吸頂、疊在前一張上面,被蓋住的卡片會逐漸縮小退到後方。

Playground

即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。

24
12
0.04
編輯中的呼叫預設屬性
<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型別預設值說明
childrenReactNode—每個直接子元素成為一張卡片
offsetTopnumber96卡片吸頂位置(px)
peeknumber12堆疊時每層向下錯開的距離(px)
scaleStepnumber0.04疊入時每層縮小的比例
containerRefObject<HTMLElement>—滾動發生在巢狀 overflow 容器內時,傳入該容器的 ref
cardClassNamestring—套用到每張卡片外層

細節

  • 以 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 時,可以從「手動」分頁複製原始碼。