Ornament Toolbar
懸浮於內容面板邊緣之外的 visionOS 風格裝飾工具列,捲動時自動縮合為膠囊、停下時展開完整控制,並帶有 Z 軸抬升與跟隨視差。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
20
360
<OrnamentToolbar />
安裝
npx shadcn@latest add https://webberui.com/r/ornament-toolbar.json或在 components.json 設定 registries 後,改用 @webberui/ornament-toolbar 安裝。
安裝依賴後,從 registry JSON(/r/ornament-toolbar.json 的 files[0].content)複製 ornament-toolbar.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge lucide-react使用
由 OrnamentToolbar 包住一個可捲動的內容面板(OrnamentContent)與一個懸浮工具列(Ornament)。工具列停靠在面板邊緣之外,捲動 OrnamentContent 時自動縮合為膠囊、停下時展開;滑鼠移入或鍵盤聚焦也會強制展開。各控制項用 OrnamentItem,群組間可插入 OrnamentSeparator。
import {
Ornament,
OrnamentContent,
OrnamentItem,
OrnamentSeparator,
OrnamentToolbar,
} from "@/components/ui/ornament-toolbar";
import { Bookmark, Minus, Plus, Share2 } from "lucide-react";
export function Example() {
return (
<OrnamentToolbar side="bottom" className="h-[280px]">
<OrnamentContent className="p-5">{/* 可捲動內容 */}</OrnamentContent>
<Ornament aria-label="文件工具列">
<OrnamentItem icon={<Minus />} aria-label="縮小" />
<OrnamentItem icon={<Plus />} label="100%" aria-label="放大" />
<OrnamentSeparator />
<OrnamentItem icon={<Bookmark />} aria-label="書籤" />
<OrnamentItem icon={<Share2 />} aria-label="分享" />
</Ornament>
</OrnamentToolbar>
);
}受控模式:傳入 collapsed 與 onCollapsedChange 自行掌握縮合狀態(此時捲動不再自動縮合)。
元件組成
| 元件 | 說明 |
|---|---|
OrnamentToolbar | 最外層容器,提供捲動偵測與縮合狀態;在此以 className 指定尺寸 |
OrnamentContent | 內容面板,本身即捲動容器,工具列依此容器的捲動而縮合/展開 |
Ornament | 懸浮於面板邊緣之外的工具列本體,role="toolbar" |
OrnamentItem | 單一控制項;有 label 時展開為藥丸,縮合時只留圖示 |
OrnamentSeparator | 控制項群組間的垂直分隔線 |
Props
OrnamentToolbar
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
side | "top" | "bottom" | "bottom" | 工具列懸浮於面板哪一側邊緣之外 |
offset | number | 20 | 工具列與面板邊緣的距離(px) |
collapsed | boolean | — | 受控的縮合狀態;傳入後捲動不再自動縮合 |
defaultCollapsed | boolean | false | 非受控模式的初始縮合狀態 |
onCollapsedChange | (collapsed: boolean) => void | — | 縮合狀態變更回呼 |
collapseOnScroll | boolean | true | 是否在捲動時自動縮合、停下時展開 |
idleDelay | number | 360 | 停止捲動後判定為靜止並展開的延遲(ms) |
className | string | — | 追加至容器;建議在此指定尺寸 |
Ornament
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
aria-label | string | "工具列" | 工具列的無障礙名稱 |
className | string | — | 追加至膠囊容器 |
OrnamentItem
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
icon | React.ReactNode | — | 控制項圖示(建議使用 lucide-react) |
label | React.ReactNode | — | 文字標籤;縮合為膠囊時平順收合 |
active | boolean | false | 作用中狀態,以 aria-pressed 標記並反白 |
aria-label | string | — | 無 label 時必填,作為圖示按鈕的無障礙名稱 |
其餘原生 <button> 屬性(onClick、disabled 等)皆會透傳。
細節
- 懸浮於邊緣之外:
Ornament以絕對定位貼齊面板的top/bottom邊緣之外並水平置中,offset控制與面板的距離。最外層容器不裁切,記得在其下方(或上方)預留空間。 - 捲動縮合、停下展開:偵測
OrnamentContent的捲動事件,捲動時縮合為膠囊、idleDelay內無捲動即展開。收合時各控制項的標籤以 spring 平順收合成純圖示膠囊。 - Z 軸抬升與跟隨視差:縮合時陰影加深、整體微縮,模擬向使用者抬升的高度;捲動速度經 spring 平滑後映射為輕微的垂直跟隨位移,形成漂浮般的視差。
- hover/focus 強制展開:滑鼠移入或鍵盤焦點落在工具列內時一律展開,確保指標與鍵盤使用者都能看到完整控制項。
可及性
Ornament為role="toolbar"並帶aria-orientation;方向鍵在控制項間移動焦點,Home/End跳至首尾。OrnamentItem的active以aria-pressed表達;無文字標籤時必須提供aria-label,有label時自動採用其文字作為無障礙名稱。- 鍵盤聚焦工具列時強制展開並顯示標籤,縮合狀態不會妨礙鍵盤操作。
- 使用者系統開啟「減少動態效果」時,停用自動縮合與跟隨視差,工具列維持展開,標籤與縮放改為即時切換。