WebberUI

View Continuity Grid

卡片網格與詳情頁之間的真實視圖切換,被點卡片的圖片與標題以 View Transitions API 跨頁延續,其餘卡片依距離向外退散、返回時依序歸位。

載入預覽⋯

Playground

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

2
64
0.5
<ViewContinuityGrid />

安裝

npx shadcn@latest add https://webberui.com/r/view-continuity-grid.json

或在 components.json 設定 registries 後,改用 @webberui/view-continuity-grid 安裝。

安裝依賴後,從 registry JSON(/r/view-continuity-grid.jsonfiles[0].content)複製 view-continuity-grid.tsx 原始碼到你的 components/ui/ 目錄:

npm install motion lucide-react clsx tailwind-merge

使用

傳入 items 陣列即可。點擊卡片會切換到該項目的詳情頁,圖片與標題以 View Transitions API 跨頁變形延續,其餘卡片向外退散;返回時依序歸位。

import {
  ViewContinuityGrid,
  type ViewContinuityItem,
} from "@/components/ui/view-continuity-grid";

const items: ViewContinuityItem[] = [
  {
    id: "aurora",
    title: "極光平原",
    eyebrow: "北緯 69°",
    image: "/images/aurora.jpg",
    description: "詳情頁的內文…",
  },
  // …
];

<ViewContinuityGrid items={items} columns={2} />;

受控模式

傳入 openIdonOpenChange 即可由外部(例如路由狀態)驅動目前展開的項目;null 代表回到網格:

const [openId, setOpenId] = React.useState<string | null>(null);

<ViewContinuityGrid items={items} openId={openId} onOpenChange={setOpenId} />;

自訂詳情內容

預設詳情頁渲染 item.description。若要完全掌控詳情頁的排版,傳入 renderDetail

<ViewContinuityGrid
  items={items}
  renderDetail={(item) => (
    <article className="prose">
      <p>{item.description}</p>
      <a href={`/posts/${item.id}`}>閱讀全文 →</a>
    </article>
  )}
/>;

Props

Prop型別預設值說明
itemsViewContinuityItem[]網格項目資料
columnsnumber2網格欄數(1–4)
openIdstring | null受控模式:目前展開項目的 id(null 為回到網格)
defaultOpenIdstring | nullnull非受控模式的初始展開 id
onOpenChange(id: string | null) => void展開/返回時觸發
spreadnumber64其餘卡片向外退散的基準距離(px)
durationnumber0.5單次過場時長(秒)
imageAspectstring"4 / 3"圖片長寬比(CSS aspect-ratio 值)
renderDetail(item: ViewContinuityItem) => ReactNode自訂詳情頁內容;未提供時渲染 item.description
backLabelstring"返回"返回按鈕文字與無障礙標籤

ViewContinuityItem

欄位型別說明
idstring唯一識別,作為 View Transition 名稱與受控值的依據
titlestring卡片與詳情頁共用、會跨頁延續的標題
imagestring圖片來源(會跨頁延續變形)
imageAltstring圖片替代文字
eyebrowstring標題上方的小標(不跨頁延續)
descriptionReactNode詳情頁內文

細節

  • 切換時以原生 document.startViewTransition() 包裹狀態更新,並搭配 flushSync 讓 DOM 在同一幀內替換,瀏覽器才能正確捕捉前後兩個快照。
  • 被點卡片的圖片與標題在兩個視圖共用同一組 view-transition-name,由瀏覽器負責跨頁變形;平時不掛名,只在過場當下才賦名,避免干擾頁面上其他的 View Transition。
  • 其餘卡片依「與被點卡片的相對格位」計算向外退散的方向與距離,較遠的卡片退得較遠;返回時以動態注入的 keyframes 依距離錯開延遲,依序歸位。
  • 不支援 View Transitions API 的瀏覽器,或使用者開啟「減少動態效果」時,直接即時切換視圖,功能完全不受影響。

可及性

  • 卡片為原生 <button>,可聚焦並以 Enter/Space 觸發;網格為 role="list"
  • 進入詳情頁時焦點移到返回鈕,返回網格時焦點歸還原本的卡片;詳情頁支援 Esc 返回。
  • 詳情頁為 role="region" 並以項目標題作為 aria-label
  • 過場只使用 view-transition-name 與 CSS 動畫,減少動態效果時完全略過動畫。

On this page