Coachmark Tour Flow
產品導覽 overlay:SVG 遮罩在目標元素之間 morph 移動聚焦框,說明卡沿最佳方位錨定並隨步進飛行跟隨,支援捲動追蹤目標位置。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
8
12
<CoachmarkTourFlow />
安裝
npx shadcn@latest add https://webberui.com/r/coachmark-tour-flow.json或在 components.json 設定 registries 後,改用 @webberui/coachmark-tour-flow 安裝。
安裝依賴後,從 registry JSON(/r/coachmark-tour-flow.json 的 files[0].content)複製 coachmark-tour-flow.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge lucide-react使用
以 steps 陣列描述導覽路徑,每個步驟指向一個目標元素(CSS 選擇器或元素 ref)。遮罩以 portal 渲染於 document.body,並固定貼齊 root(可捲動容器)的可視區;不傳 root 時則以整個視口為基準。
import * as React from "react";
import { CoachmarkTourFlow } from "@/components/ui/coachmark-tour-flow";
export function Example() {
const [open, setOpen] = React.useState(false);
return (
<>
<button data-tour="search" onClick={() => setOpen(true)}>
開始
</button>
<CoachmarkTourFlow
open={open}
onOpenChange={setOpen}
steps={[
{
target: "[data-tour='search']",
title: "全域搜尋",
content: "從任何頁面按下即可搜尋。",
},
{
target: "[data-tour='settings']",
title: "偏好設定",
content: "在這裡調整主題與通知。",
placement: "top",
},
]}
/>
</>
);
}open 與 step 皆支援受控/非受控雙模式:傳入對應 prop 即為受控,否則由元件內部管理(可用 defaultOpen、defaultStep 設定初值)。
Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
steps | CoachmarkStep[] | — | 依序導覽的步驟清單 |
open | boolean | — | 受控:是否開啟導覽 |
defaultOpen | boolean | false | 非受控預設開啟狀態 |
onOpenChange | (open) => void | — | 開啟狀態變更回呼 |
step | number | — | 受控:目前步驟索引 |
defaultStep | number | 0 | 非受控預設步驟索引 |
onStepChange | (index) => void | — | 步驟索引變更回呼 |
onComplete | () => void | — | 走完最後一步、再按「完成」時觸發 |
root | RefObject<HTMLElement> | — | 定位/捲動基準容器;不傳則以視口為準 |
spotlightPadding | number | 8 | 聚焦框在目標周圍的留白(px) |
spotlightRadius | number | 12 | 聚焦框圓角(px) |
overlayColor | string | rgba(10,10,10,0.6) | 遮罩壓暗色 |
scrollIntoView | boolean | true | 步驟切換時自動把目標捲入可視區 |
showProgress | boolean | true | 顯示步驟頁碼與圓點 |
dismissOnBackdropClick | boolean | false | 點擊遮罩空白處關閉導覽 |
labels | { next; prev; done; skip } | — | 各動作按鈕文案 |
className | string | — | 追加到最外層遮罩容器 |
cardClassName | string | — | 追加到說明卡 |
CoachmarkStep
| 欄位 | 型別 | 說明 |
|---|---|---|
target | string | RefObject<HTMLElement> | 目標元素:CSS 選擇器(於 root 或 document 內查找)或元素 ref |
title | React.ReactNode | 步驟標題 |
content | React.ReactNode | 步驟說明內容 |
placement | "top" | "bottom" | "left" | "right" | "auto" | 說明卡貼齊方位,覆寫預設 auto |
padding | number | 此步驟的聚焦框留白,覆寫全域設定 |
細節
- 遮罩 morph:聚焦框透過 SVG
<mask>在整片壓暗層上挖出圓角矩形,框的x/y/width/height/rx皆綁定彈簧 motion value,步驟切換時平滑 morph 到下一個目標。 - 飛行跟隨:說明卡位置由聚焦框的彈簧值即時推導(
useTransform),因此步進時卡片會沿最佳方位飛到新目標旁,捲動時也一路跟隨。 - 捲動追蹤:開啟期間監聽
root與視窗的捲動、resize及目標/容器的ResizeObserver,以requestAnimationFrame節流持續量測目標位置;目標被捲出時聚焦框會停留在可視邊緣。 - 最佳方位:
placement為auto時依四周可用空間自動挑選貼齊側,卡片位置並夾在遮罩邊界內避免溢出。
可及性
- 說明卡為
role="dialog",以aria-labelledby/aria-describedby關聯標題與內容,開啟時自動移入焦點。 - 鍵盤操作:
Esc關閉、→下一步、←上一步;所有動作皆有原生按鈕可聚焦觸發。 - 使用者開啟「減少動態效果」時,聚焦框改為瞬移就位、淡入淡出縮短,捲動與過渡不再有彈簧與平滑效果,版面與功能維持一致。