版面構圖

Era Strata Layout

把年代/階段疊成水平地層帶的手風琴式版面,展開層垂直撐開、其餘壓縮,層內內容帶視差浮出。

這是 WebberUI Pro 元件

下方的安裝指令需要授權才能執行,未授權時會回 401。

怎麼安裝 Pro 元件 →查看方案 →

把這個效果裝進你的專案

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

npx webberui@latest add "https://webberui.com/r/era-strata-layout.json?t=<安裝 token>"

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

Era Strata Layout 是用 React、Motion 與 Tailwind CSS 做的垂直手風琴時間軸(timeline)版面:每個年代或階段是一條水平地層帶,可用點擊、懸停或頁面捲動切換展開的那一層。

Playground

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

440
72
編輯中的呼叫預設屬性
<EraStrataLayout/>

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

適用情境

  • 「關於我們」頁的公司沿革與里程碑
  • 產品發展史、版本演進的分期介紹
  • 品牌故事或長篇專題中按年代推進的章節
  • 課程、導入流程這類有先後順序的階段說明
  • 不適合:問答或設定這類各段落互不相關、可能同時展開多段的內容,請改用 Accordion

安裝

npx webberui@latest add "https://webberui.com/r/era-strata-layout.json?t=<安裝 token>"

或在 components.json 設定 registries 後,改用 @webberui/era-strata-layout 安裝。

使用

import {
  EraStrataLayout,
  type EraStratumData,
} from "@/components/ui/era-strata-layout";

const eras: EraStratumData[] = [
  {
    id: "1998",
    era: "1998",
    title: "車庫創業",
    summary: "三個人、一台伺服器。",
    content: "第一個版本在深夜上線。",
    accentClassName: "bg-amber-400 dark:bg-amber-500",
  },
  // ...
];

<EraStrataLayout items={eras} height={440} defaultActive={0} />;

trigger 可切換觸發方式:click(預設)、hover 懸停展開,或 scroll 隨頁面捲動逐層推進。

// 隨捲動推進:捲進巢狀容器時把該容器 ref 傳入 container
<EraStrataLayout items={eras} trigger="scroll" height={480} />

範例

用外部按鈕控制展開層

傳入 active 與 onActiveChange 進入受控模式,可以搭配上一步/下一步按鈕(受控模式在 click 與 hover 觸發下有效)。

"use client";

import * as React from "react";
import { EraStrataLayout, type EraStratumData } from "@/components/ui/era-strata-layout";

export function CompanyHistory({ eras }: { eras: EraStratumData[] }) {
  const [active, setActive] = React.useState(0);

  return (
    <div className="space-y-4">
      <EraStrataLayout items={eras} active={active} onActiveChange={setActive} />
      <div className="flex gap-2">
        <button onClick={() => setActive((i) => Math.max(0, i - 1))}>上一個階段</button>
        <button onClick={() => setActive((i) => Math.min(eras.length - 1, i + 1))}>
          下一個階段
        </button>
      </div>
    </div>
  );
}

懸停展開並放入自訂內容

content 與 summary 接受任何 React 節點,可以放清單或圖片;trigger="hover" 讓滑鼠移過或鍵盤聚焦時就展開。

const roadmap: EraStratumData[] = [
  {
    id: "q1",
    era: "Q1",
    title: "公開測試",
    summary: "開放 500 位早期使用者。",
    content: (
      <ul className="list-disc pl-5 text-sm">
        <li>帳號與團隊權限</li>
        <li>基本報表匯出</li>
      </ul>
    ),
    accentClassName: "bg-sky-500",
  },
  {
    id: "q2",
    era: "Q2",
    title: "正式上線",
    summary: "開放所有使用者註冊。",
    accentClassName: "bg-emerald-500",
  },
];

<EraStrataLayout items={roadmap} trigger="hover" height={360} collapsedSize={64} />

在巢狀捲動容器中隨捲動推進

捲動發生在自己的 overflow 容器裡而不是整頁時,把容器的 ref 傳給 container。

"use client";

import * as React from "react";
import { EraStrataLayout, type EraStratumData } from "@/components/ui/era-strata-layout";

export function ScrollPanel({ eras }: { eras: EraStratumData[] }) {
  const panelRef = React.useRef<HTMLDivElement>(null);

  return (
    <div ref={panelRef} className="h-[480px] overflow-y-auto">
      <EraStrataLayout items={eras} trigger="scroll" height={480} container={panelRef} />
    </div>
  );
}

Props

Prop型別預設值說明
itemsEraStratumData[]—地層資料,依序由上而下堆疊
activenumber—受控的展開索引(click/hover 模式生效)
defaultActivenumber0非受控模式的初始展開索引
onActiveChange(index: number) => void—展開索引變更時觸發
trigger"click" | "hover" | "scroll""click"觸發展開的方式
heightnumber | string440版面總高度(數字為 px)
collapsedSizenumber72收合地層/標題列的高度(px)
parallaxbooleantrue內容是否帶視差浮出
containerRefObject<HTMLElement | null>—scroll 模式的巢狀捲動容器 ref
labelstring"年代地層"無障礙標籤
classNamestring—外層容器 class
stratumClassNamestring—套用在每個地層帶上的 class

EraStratumData

欄位型別說明
idstring穩定識別碼,作為無障礙錨點與 key
eraReactNode年代/階段標記,收合時仍可見
titleReactNode章節標題,收合時仍可見
summaryReactNode展開時最先浮出的一句摘要
contentReactNode展開時帶視差浮出的主要內容
accentClassNamestring左側地層色條的強調色 class

細節

  • 展開層以彈簧轉場垂直撐開,其餘地層同步壓縮回 collapsedSize,只留標題列可見
  • 使用 flexbox 的 flex-grow 分配剩餘空間,同一時間恰有一層展開;請確保 height ≥ collapsedSize × 地層數
  • 層內 summary 與 content 以不同起始位移依序浮出,形成視差層次;parallax={false} 時僅保留淡入
  • scroll 模式會撐出對應的捲動高度並釘住視窗,捲動進度映射到展開的地層;點擊某層會平滑捲動至該層

可及性

  • 每個地層標題為原生 button,帶 aria-expanded 與 aria-controls,內容區為 role="region" 並以標題 aria-labelledby
  • 標題列採 roving tabindex:Tab 進入後以 ↑/↓(或 ←/→)在各層間移動,Home/End 跳至首尾,Enter/Space 展開
  • 收合的內容區標記 inert 與 aria-hidden,不會被鍵盤或輔助科技誤入
  • 使用者系統開啟「減少動態效果」時,改為全展開的靜態清單,所有內容一次呈現、不做壓縮或動畫

常見問題

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

可以。元件檔案開頭已有 "use client",在 Server Component 頁面中照常 import 即可。開啟「減少動態效果」時,元件會在掛載後才切換成靜態版,首次渲染與 SSR 輸出一致。

scroll 模式會佔多少頁面高度?

外層高度為 height 乘以地層數,並以 sticky 釘住一個 height 高的視窗,捲動進度依序對應到每一層。scroll 模式需要數值高度,傳入字串時會以 440 推算捲動距離。

為什麼地層擠在一起或內容被切掉?

收合的地層固定為 collapsedSize 高,展開層分到剩下的空間。請確保 height 至少是 collapsedSize 乘以地層數,並留給展開層足夠的內容高度。

怎麼改顏色?

每一層的左側色條用 accentClassName 指定,例如 "bg-amber-400 dark:bg-amber-500";整體外觀可以用 className 與套用在每一層的 stratumClassName 調整。

支援鍵盤與螢幕閱讀器嗎?

支援。標題列是帶 aria-expanded 的原生按鈕,方向鍵在各層間移動、Home/End 跳至首尾;收合的內容區標記 inert,不會被誤入。