頁面區塊 Sections
手繪筆記定價區塊
筆記本紙張風格的三欄方案卡 React 元件:手繪邊框與勾選記號逐筆描出,推薦方案有手繪圈選標註,月繳/年繳切換時價格補間,舊價格畫上刪除線後淡出。
一個像攤開筆記本的定價區塊。每張方案卡是橫線筆記紙——淡藍灰橫線、左側紅色邊距線、頂端三個活頁孔;邊框不用 CSS border,而是依卡片實際尺寸畫一條帶抖動的 SVG 路徑,進場時一筆一筆描出,接著功能勾逐個描出,最後推薦方案外圍的手繪圈與箭頭、手寫標註才出現。上方像便利貼的月繳/年繳切換,選中格底下的手繪圈會滑過去;切換時價格以補間變化,舊價格浮起、被畫上刪除線再淡出,年繳時並排顯示刪去的原價。筆觸由 seed 決定,同一個 seed 每次渲染都一樣,SSR 與瀏覽器不會對不上。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add sketchnote-pricingPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
5
260
編輯中的呼叫預設屬性
<SketchnotePricing/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/sketchnote-pricing.json或在 components.json 設定 registries 後,改用 @webberui/sketchnote-pricing 安裝。
使用
import { SketchnotePricing } from "@/components/ui/sketchnote-pricing";
<SketchnotePricing
eyebrow="方案"
title="挑一本適合你的筆記本"
subtitle="年繳等於送你兩個月。"
plans={[
{
id: "starter",
name: "入門",
price: { monthly: 0, yearly: 0 },
features: [
{ label: "每月 3 堂公開課", included: true },
{ label: "完整課程庫", included: false },
],
cta: { label: "免費開始", href: "/signup" },
},
{
id: "plus",
name: "進階",
price: { monthly: 290, yearly: 2900 },
features: [
{ label: "完整課程庫", included: true },
{ label: "講師問答", included: true },
],
cta: { label: "升級進階", onClick: () => console.log("upgrade") },
recommended: true,
note: "最多人選",
},
]}
onBillingChange={(billing) => console.log(billing)}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
plans | SketchnotePricingPlan[] | — | 方案清單,建議三張;窄螢幕直排、md 以上三欄 |
billing | "monthly" | "yearly" | — | 受控的計費週期;未提供時由元件內部管理 |
defaultBilling | "monthly" | "yearly" | "monthly" | 非受控模式的初始計費週期 |
onBillingChange | (billing: "monthly" | "yearly") => void | — | 切換計費週期時回呼 |
currency | string | "NT$" | 價格前的貨幣前綴 |
yearlyNote | string | "省 2 個月" | 切換器旁的手寫小字,傳空字串可隱藏 |
eyebrow | ReactNode | — | 標題上方的小標籤 |
title | ReactNode | — | 主標題,下方描出手繪波浪底線;有提供時作為區塊的 aria-labelledby |
subtitle | ReactNode | — | 副標 |
seed | number | 5 | 手繪抖動的亂數種子,同一個值永遠畫出相同筆觸 |
grain | boolean | false | 在紙張上疊一層 feTurbulence 噪點 |
className | string | — | 透傳到最外層 section |
SketchnotePricingPlan
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 方案唯一識別碼 |
name | string | — | 方案名稱(輕微傾斜,像手寫標題) |
description | string | — | 方案簡短說明 |
price | { monthly: number; yearly: number } | — | monthly 為每月金額,yearly 為一年總額;年繳低於 12 個月總價時會顯示刪去的原價 |
features | { label: string; included: boolean }[] | — | 功能清單;included: false 以手繪刪除線與淡色呈現 |
cta | { label: string; href?: string; onClick?: () => void } | — | 行動按鈕;有 href 時渲染為連結,否則為按鈕 |
recommended | boolean | false | 推薦方案:卡片微傾、略放大,外圍手繪圈選與箭頭標註 |
note | string | "最多人選" | 推薦方案旁的手寫標註文字 |
CSS 變數
| 變數 | 預設值 | 說明 |
|---|---|---|
--wb-sketchnote-pricing-accent | #0f766e | 強調色:勾、圈選、箭頭、底線、推薦方案按鈕 |
--wb-sketchnote-pricing-accent-dark | #2dd4bf | 深色模式的強調色 |
--wb-sketchnote-pricing-paper | #fffdf5 | 筆記紙底色 |
--wb-sketchnote-pricing-paper-dark | #171717 | 深色模式紙張底色(neutral-900),橫線同時調暗 |
--wb-sketchnote-pricing-radius | 14px | 卡片圓角,手繪邊框也依此值轉角 |
--wb-sketchnote-pricing-duration | 260ms | 描線時間單位:卡框 3 倍、每個勾 1 倍、推薦圈 3 倍 |
細節
- 進場順序:區塊捲入視窗後,卡框以
pathLength從 0 描到 1(時長單位 × 3,卡與卡間隔 120ms),接著每個功能勾描出(單位 × 1,間隔 60ms),全部完成後才描推薦圈與箭頭;緩動統一為cubic-bezier(0.65, 0, 0.35, 1)。 - 手繪邊框依卡片實際尺寸以
ResizeObserver重畫,每段約 48px 加一次 ±1.5px 的垂直抖動,結尾略越過起點,像沒收好的筆。每張卡、每個勾都用seed加序號產生自己的筆觸。 - 滑過卡片時以 spring(stiffness 300、damping 20)上浮 4px,推薦卡轉回水平、其他卡微傾 ±0.6°。
- 時長單位在掛載時讀取
--wb-sketchnote-pricing-duration,因此 CSS 覆寫同時影響 JS 驅動的描線動畫;執行期改變變數後需重新掛載元件才會生效。
可及性
- 最外層為
section,有標題時以aria-labelledby指向標題;每張方案卡是article,以aria-labelledby指向方案名稱 - 月繳/年繳切換為
role="radiogroup",選項為role="radio"搭配aria-checked;採 roving tabindex,方向鍵、Home、End 可切換 - 推薦方案除了視覺圈選,名稱後另有螢幕閱讀器專用的「推薦方案」字樣;不含的功能前綴「不含」
- 價格變動時以
aria-live="polite"朗讀最終價格與原價;補間中的數字與所有手繪 SVG 對輔助科技隱藏 - 使用者系統開啟「減少動態效果」時,所有描線直接完整呈現,價格直接切換不補間,也停用滑過卡片的位移