WebberUI

Isotype Proportion Grid

「每 X 中有 Y」的圖標比例方陣:數百個小圖示隨滾動以波浪方式變色點亮,並逐步驟切換不同的比例敘述。

載入預覽⋯

Playground

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

100
10
0.14
<IsotypeProportionGrid />

安裝

npx shadcn@latest add https://webberui.com/r/isotype-proportion-grid.json

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

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

npm install motion clsx tailwind-merge

使用

import {
  IsotypeProportionGrid,
  type ProportionStep,
} from "@/components/ui/isotype-proportion-grid";

const steps: ProportionStep[] = [
  { numerator: 90, denominator: 100, label: "90% 每天使用手機", color: "#6366f1" },
  { numerator: 63, denominator: 100, label: "63% 通勤時聽音樂", color: "#14b8a6" },
  { numerator: 27, denominator: 100, label: "27% 每週閱讀", color: "#f59e0b" },
];

<IsotypeProportionGrid steps={steps} total={100} columns={10} />;

元件會撐出 stepScrollHeight × 步驟數 的捲動高度,並以 sticky 面板釘住方陣。捲動時,填充前緣依 waveOrder 掃過整個網格,對應比例的圖示逐一由中性色變為點亮色;比例下降時,多餘的圖示以反向波紋熄滅。頂部文案在跨越步驟邊界時交叉淡入。

巢狀捲動容器

若元件放在具 overflow 的容器內(而非整頁捲動),將該容器的 ref 傳入 container,並用 viewportClassName 指定 sticky 面板高度以貼合容器:

const scrollerRef = React.useRef<HTMLDivElement>(null);

<div ref={scrollerRef} className="h-[320px] overflow-y-auto">
  <IsotypeProportionGrid
    steps={steps}
    container={scrollerRef}
    stepScrollHeight="240px"
    viewportClassName="h-[320px]"
  />
</div>;

自訂圖示與掃掠方向

icon 接受任何 { className } 圖示元件(含 lucide-react),可用 waveOrder 切換波紋的掃掠方式:

import { Heart } from "lucide-react";

<IsotypeProportionGrid steps={steps} icon={Heart} waveOrder="radial" />;

Props

Prop型別預設值說明
stepsProportionStep[]各步驟的比例敘述,隨滾動依序切換
totalnumber100圖示方陣的總格數
columnsnumber10網格欄數
iconSizenumber20單一圖示邊長(px)
gapnumber6圖示間距(px)
iconIsotypeGlyph內建人形預設圖示元件(可傳 lucide-react)
activeColorstring"#6366f1"點亮顏色,可被步驟的 color 覆寫
waveOrder"row" | "column" | "diagonal" | "radial""diagonal"波次點亮的掃掠方向
waveWidthnumber0.14波峰寬度(同時過渡的圖示佔比,0–1)
revealFractionnumber0.6每個步驟視窗用於掃掠的比例,其餘停留
stepScrollHeightstring"90svh"每步驟分配的捲動高度(CSS 長度)
showDotsbooleantrue是否顯示可點擊跳轉的步驟指示點
onStepChange(index: number, step: ProportionStep) => void目前步驟變更時的回呼
containerRefObject<HTMLElement | null>巢狀捲動容器的 ref;省略時以視窗為捲動容器
aria-labelstring"圖標比例格"無障礙標籤
classNamestring套用在最外層捲動包裹容器
gridClassNamestring套用在網格容器
viewportClassNamestring"h-[100svh]"套用在 sticky 檢視面板(用於指定其高度)

ProportionStep

欄位型別說明
numeratornumber分子:每組中命中的數量
denominatornumber分母:每組總數
labelReactNode主標題;省略時自動生成「每 {denominator} 中有 {numerator}
captionReactNode補充說明文字
colorstring此步驟的點亮顏色,覆寫 activeColor
iconIsotypeGlyph此步驟的圖示,覆寫元件層級 icon

細節

  • 每格的排名 rank = 排序後位置 / total,其掃掠順序由 waveOrder 決定(diagonal 對角、radial 由中心外擴等)。當填充比例跨過某格的 rank,恰好點亮對應數量的圖示,因此「63%」即精準點亮 round(0.63 × total) 格。
  • 捲動進度以分段映射轉為填充比例:每個步驟視窗的前 revealFraction 用於掃掠到新比例,其餘用於停留,因此文案穩定時方陣也維持在完整比例。
  • 點亮色由網格父層以 animate 帶動,步驟間換色時平滑補間;各格的顯露則純由捲動位置驅動,向上捲回會反向倒帶。
  • 下方指示點可點擊,會平滑捲動到對應步驟的停留區(自動判斷是視窗或巢狀容器捲動)。

可及性

  • aria-live="polite" 的隱藏區域,於步驟切換時朗讀目前的比例敘述。
  • 方陣本身為裝飾性重複圖形,標記 aria-hidden,資訊改由文案與 live 區域傳達。
  • 步驟指示點為具 aria-label 的按鈕,標示目前步驟(aria-current)並具鍵盤聚焦樣式。
  • 使用者系統開啟「減少動態效果」時,改為靜態堆疊版面:每個步驟各自呈現正確比例的方陣,不套用 sticky 與波次動畫。

On this page