Callout Stat Hero
以單一巨型數字撐滿首屏的 hero 版面,SVG 引導線隨捲動逐條描繪,將數字各部位連向周邊註解卡並依序浮現。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<CalloutStatHero />
安裝
npx shadcn@latest add https://webberui.com/r/callout-stat-hero.json或在 components.json 設定 registries 後,改用 @webberui/callout-stat-hero 安裝。
安裝依賴後,從 registry JSON(/r/callout-stat-hero.json 的 files[0].content)複製 callout-stat-hero.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
import { CalloutStatHero } from "@/components/ui/callout-stat-hero";
<CalloutStatHero
eyebrow="2025 客戶滿意度"
value="94%"
caption="推薦我們的受訪客戶比例"
annotations={[
{ x: 8, y: 34, side: "left", title: "連續 5 季成長" },
{ x: 20, y: 76, side: "left", title: "12,480 份回饋" },
{ x: 92, y: 30, side: "right", title: "NPS 62" },
{ x: 82, y: 78, side: "right", title: "首次回應 < 2h" },
]}
/>預設以整個視窗為捲動容器(h-screen 舞台)。若放進巢狀的 overflow-y-auto 容器,請把該容器的 ref 傳給 container,並用 stageClassName 覆寫舞台高度為容器可視高度:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[480px] overflow-y-auto">
<CalloutStatHero
value="94%"
annotations={annotations}
container={scrollerRef}
stageClassName="h-[480px]"
/>
</div>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | React.ReactNode | — | 撐滿首屏的巨型數字 |
eyebrow | React.ReactNode | — | 數字上方的小標籤 |
caption | React.ReactNode | — | 數字下方的說明文字 |
annotations | StatAnnotation[] | — | 環繞數字的註解清單,捲動時依序描繪並浮現 |
connector | "straight" | "elbow" | "straight" | 引導線樣式:直線或帶水平引入段的折線 |
scrollScale | number | 依註解數估算 | 捲動軌道高度為釘住舞台高度的倍數 |
offsetTop | number | 0 | 釘住舞台的吸頂位置(px) |
container | RefObject<HTMLElement | null> | — | 巢狀捲動容器的 ref;預設以視窗為捲動容器 |
stageClassName | string | "h-screen" | 釘住舞台容器的 class;巢狀捲動時覆寫為容器可視高度 |
numberClassName | string | — | 覆寫巨型數字的 class(字級、字重、顏色…) |
aria-label | string | "數據主視覺" | 整個區塊的無障礙名稱 |
StatAnnotation
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
x | number | — | 錨點的水平位置(數字框寬度百分比,0–100) |
y | number | — | 錨點的垂直位置(數字框高度百分比,0–100) |
title | React.ReactNode | — | 註解卡標題 |
description | React.ReactNode | — | 註解卡內文(可選) |
side | "left" | "right" | 依 x 自動判定 | 卡片停靠左或右邊欄 |
id | string | 索引 | 自訂 React key |
細節
- 區塊以
position: sticky釘住整個舞台,外層軌道高度依scrollScale撐開,捲動距離即為引導線逐條描繪的進度來源。 - 引導線的端點在掛載後量測 DOM(卡片內緣中點 → 數字錨點),以
ResizeObserver追蹤重排,換算為舞台座標繪製,因此文字始終銳利、不會被縮放模糊。 - 引線以
pathLength描繪,錨點小圓在該條引線描完後才彈入,註解卡則以透明度與微幅位移浮現,形成「描線 → 亮點 → 出卡」的節奏。 x/y是相對於數字元素邊界框的百分比,可精準指向某個字元或角落;靠近0或100的錨點請預留邊距,避免被overflow-hidden舞台裁切。
可及性
- 開啟系統「減少動態效果」時,改渲染不含捲動與引導線的靜態摘要:巨型數字加上依序編號的註解列表,資訊完整可讀。
- 引導線
svg、錨點與起點小圓皆標記aria-hidden,不干擾輔助科技;數字與註解文字維持正常朗讀。 - 舞台容器帶
role="group"與aria-label,可作為單一可辨識的區塊被導覽。