輪播

堆疊窺視輪播

卡片堆疊後層窺視的輪播,滑動時後卡遞補上前。

當前卡片置中顯示,後方 2–3 張向上縮小偏移、露出可窺視的邊緣;拖曳或按鈕切換時前卡帶旋轉飛出、後卡以 spring 遞補上前,支援首尾循環與圓點指示,適合行程卡、商品卡等精選內容瀏覽。

把這個效果裝進你的專案

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

npx webberui@latest add stacked-peek-carousel

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

Stacked Peek Carousel 是用 React、Tailwind CSS 與 Motion 做的堆疊卡片輪播(stacked carousel)元件:後方卡片向上縮小、露出邊緣,拖曳或按按鈕時前卡帶旋轉飛出,後卡以 spring 遞補;左右箭頭圖示來自 lucide-react。

Playground

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

2
16
0.06
編輯中的呼叫預設屬性
<StackedPeekCarousel/>

完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。

適用情境

  • 旅遊、活動或課程的精選卡片,一次專注介紹一個項目。
  • 電商首頁的推薦商品或新品區塊,行動版用拖曳切換。
  • 作品集或客戶案例的精簡展示,空間有限時取代橫向列表。
  • App 介紹頁的功能卡片,搭配下方說明文字同步切換。
  • 不適合:需要自動播放與分段進度條時,改用 Vertical Story Carousel。

安裝

npx webberui@latest add https://webberui.com/r/stacked-peek-carousel.json

或在 components.json 設定 registries 後,改用 @webberui/stacked-peek-carousel 安裝。

使用

import { StackedPeekCarousel } from "@/components/ui/stacked-peek-carousel";

<StackedPeekCarousel
  items={[
    <TripCard key="kyoto" title="京都・嵐山" />,
    <TripCard key="swiss" title="瑞士・少女峰" />,
    <TripCard key="palau" title="帛琉・洛克群島" />,
  ]}
  peek={2}
  onIndexChange={(index) => console.log("目前在第", index + 1, "張")}
/>

每個 item 會鋪滿舞台區域(預設 h-72 w-64,可用 stageClassName 覆寫),建議卡片本身設定 h-full w-full 與圓角背景。

範例

商品推薦卡片

items 接受任意 ReactNode 陣列,每個節點都要有 key。用 stageClassName 調整卡片舞台尺寸,卡片本身設 h-full w-full。

import { StackedPeekCarousel } from "@/components/ui/stacked-peek-carousel";

const products = [
  { id: "kettle", name: "手沖細口壺", price: "NT$1,280", image: "/products/kettle.jpg" },
  { id: "grinder", name: "手搖磨豆機", price: "NT$2,450", image: "/products/grinder.jpg" },
  { id: "dripper", name: "陶瓷濾杯", price: "NT$680", image: "/products/dripper.jpg" },
];

<StackedPeekCarousel
  stageClassName="h-96 w-72"
  items={products.map((p) => (
    <a
      key={p.id}
      href={`/products/${p.id}`}
      className="flex h-full w-full flex-col overflow-hidden rounded-2xl border bg-white shadow-md"
    >
      <img src={p.image} alt="" className="h-3/4 w-full object-cover" draggable={false} />
      <div className="p-4">
        <p className="font-medium">{p.name}</p>
        <p className="text-sm text-neutral-500">{p.price}</p>
      </div>
    </a>
  ))}
/>

同步顯示說明文字

元件內部管理目前索引,透過 onIndexChange 把索引同步到外部狀態,就能在輪播下方顯示對應的說明。直接傳入 setIndex 可保持回呼參考穩定。

"use client";

import * as React from "react";
import { StackedPeekCarousel } from "@/components/ui/stacked-peek-carousel";

const features = [
  { id: "sync", title: "多裝置同步", body: "手機、平板與電腦的資料即時同步。" },
  { id: "share", title: "共用清單", body: "邀請家人一起編輯同一份清單。" },
  { id: "remind", title: "到點提醒", body: "抵達指定地點時自動跳出提醒。" },
];

export function FeatureTour() {
  const [index, setIndex] = React.useState(0);

  return (
    <div className="space-y-4 text-center">
      <StackedPeekCarousel
        onIndexChange={setIndex}
        items={features.map((f) => (
          <div key={f.id} className="flex h-full w-full items-center justify-center rounded-2xl bg-neutral-900 text-lg text-white">
            {f.title}
          </div>
        ))}
      />
      <p className="text-sm text-neutral-600">{features[index].body}</p>
    </div>
  );
}

不循環、加深堆疊

loop={false} 時到最後一張就停止,箭頭會變成停用狀態;peek、offset 與 scaleStep 調整後方露出的層數、間距與縮小比例。

<StackedPeekCarousel
  items={cards}
  loop={false}
  showDots={false}
  peek={3}
  offset={12}
  scaleStep={0.05}
/>

Props

Prop型別預設值說明
itemsReact.ReactNode[]—輪播卡片內容清單,每個節點鋪滿舞台區域
peeknumber2後方可窺視的堆疊層數
offsetnumber16每層堆疊向上偏移的距離(px)
scaleStepnumber0.06每層堆疊縮小的比例(0–1)
loopbooleantrue是否首尾循環
showDotsbooleantrue是否顯示圓點指示器
showArrowsbooleantrue是否顯示左右切換按鈕
stageClassNamestring—舞台(卡片堆疊區)的尺寸樣式,預設 h-72 w-64
onIndexChange(index: number) => void—目前索引變更時的回呼
classNamestring—透傳到最外層容器

可及性

  • 舞台標記為 role="region" 搭配 aria-roledescription="carousel",並在標籤中標示總張數
  • 目前位置以 aria-live="polite" 區塊即時播報「第 X 張,共 N 張」
  • 後方窺視卡片以 aria-hidden 對輔助科技隱藏,並以 pointer-events-none 停用互動
  • 左右按鈕與圓點皆為原生 button,可鍵盤聚焦與操作,各自附上「上一張/下一張/跳至第 N 張」的 aria-label,當前圓點標記 aria-current
  • 使用者系統開啟「減少動態效果」時停用拖曳手勢與彈簧動畫,切換改為即時完成

常見問題

可以在 Next.js App Router 使用嗎?

可以。元件檔案開頭已有 "use client",在 Server Component 頁面直接 import 即可。只是 items 是 React 節點陣列,傳 onIndexChange 這類函式時,外層要是 Client Component。

可以從外部指定目前顯示第幾張嗎?

不行。元件沒有 index 這類受控 prop,也沒有自動播放;目前索引由內部管理,外部只能透過 onIndexChange 得知變化。使用者可以拖曳、點左右按鈕或點圓點切換。

往右拖為什麼也是下一張?

拖曳的位移加上速度加權超過門檻時一律前進到下一張,拖曳方向只決定前卡往哪一側飛出。要回到上一張請用左箭頭或圓點。

鍵盤可以操作嗎?

左右按鈕與圓點都是原生 button,可用 Tab 聚焦並以 Enter/空白鍵操作;舞台本身沒有綁定方向鍵。系統開啟「減少動態效果」時會停用拖曳與彈簧動畫,改為即時切換。

需要哪些依賴?

依賴是 motion、lucide-react(箭頭圖示)、clsx 與 tailwind-merge。不用 CLI 的話,從安裝區塊的「手動」分頁複製原始碼即可。