導航
應用選單列
桌面應用式頂部選單列:開啟一個選單後 hover 相鄰項即直接切換,面板滑動到新位置而非關開,方向鍵完整巡覽。
像桌面應用程式頂端那條「檔案/編輯/檢視」選單列。點開任一選單後,指標滑到相鄰的選單名稱就直接切換,不必再點;切換時不是舊面板關掉、新面板重開,而是同一個面板以彈簧動畫滑到新位置、大小跟著變,內容交叉淡接。項目層與下拉選單同款:一般項目、核取、單選、子選單、群組標題、分隔線與快捷鍵標示都有,鍵盤可用 ←/→ 在選單間移動、↑/↓ 巡覽項目。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/menubar.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
100
500
150
編輯中的呼叫預設屬性
<Menubar/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/menubar.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/menubar 安裝。
使用
import { Menubar } from "@/components/ui/menubar";
const [lineNumbers, setLineNumbers] = React.useState(true);
const [fontSize, setFontSize] = React.useState("md");
<Menubar>
<Menubar.Menu value="file">
<Menubar.Trigger>檔案</Menubar.Trigger>
<Menubar.Content>
<Menubar.Item onSelect={() => console.log("新增")}>
新增
<Menubar.Shortcut>⌘N</Menubar.Shortcut>
</Menubar.Item>
<Menubar.Sub>
<Menubar.SubTrigger>最近開啟</Menubar.SubTrigger>
<Menubar.SubContent>
<Menubar.Item>週會記錄.md</Menubar.Item>
<Menubar.Item>旅行清單.md</Menubar.Item>
</Menubar.SubContent>
</Menubar.Sub>
<Menubar.Separator />
<Menubar.Item>匯出為 PDF</Menubar.Item>
</Menubar.Content>
</Menubar.Menu>
<Menubar.Menu value="view">
<Menubar.Trigger>檢視</Menubar.Trigger>
<Menubar.Content>
<Menubar.CheckboxItem checked={lineNumbers} onCheckedChange={setLineNumbers}>
顯示行號
</Menubar.CheckboxItem>
<Menubar.Separator />
<Menubar.Label inset>字級</Menubar.Label>
<Menubar.RadioGroup value={fontSize} onValueChange={setFontSize}>
<Menubar.RadioItem value="sm">小</Menubar.RadioItem>
<Menubar.RadioItem value="md">中</Menubar.RadioItem>
<Menubar.RadioItem value="lg">大</Menubar.RadioItem>
</Menubar.RadioGroup>
</Menubar.Content>
</Menubar.Menu>
</Menubar>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | string | — | 受控:目前開啟的 Menu 的 value,'' 表示全部關閉 |
defaultValue | string | '' | 非受控模式的初始開啟 Menu |
onValueChange | (value: string) => void | — | 開啟的 Menu 變動時回呼(全關時傳 '') |
loop | boolean | true | 方向鍵到頭尾時是否循環(Trigger 之間與選單項目之間都適用) |
duration | number | 100 | 開關與內容交叉淡接的動畫時長(毫秒) |
switchStiffness | number | 500 | 切換選單時面板滑動的彈簧剛度 |
switchDamping | number | 40 | 切換選單時面板滑動的彈簧阻尼 |
subDelay | number | 150 | 指標停在子選單項目上多久後展開(毫秒) |
className | string | — | 透傳到選單列根元素 |
Menubar.Menu
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | string | — | 這個選單的唯一值(必填),用於 value/onValueChange |
Menubar.Trigger
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
disabled | boolean | false | 停用:無法開啟,左右鍵巡覽時跳過 |
className | string | — | 透傳到觸發鈕;其餘 button 屬性也會透傳 |
Menubar.Content
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
className | string | — | 透傳到選單面板(例如 min-w-56 調整最小寬度) |
Menubar.Item
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
onSelect | () => void | — | 選取時回呼(點擊、Enter 或 Space) |
disabled | boolean | false | 停用:無法選取,鍵盤巡覽時跳過 |
inset | boolean | false | 左側留出與核取項目對齊的 icon 空間 |
textValue | string | — | typeahead 比對用文字;預設取項目的文字內容 |
closeOnSelect | boolean | true | 選取後是否關閉選單 |
className | string | — | 透傳到項目 |
Menubar.CheckboxItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
checked | boolean | — | 受控的勾選狀態 |
defaultChecked | boolean | false | 非受控模式的初始勾選狀態 |
onCheckedChange | (checked: boolean) => void | — | 勾選狀態變動時回呼 |
onSelect | () => void | — | 選取時回呼 |
disabled | boolean | false | 停用 |
textValue | string | — | typeahead 比對用文字 |
closeOnSelect | boolean | false | 選取後是否關閉選單(核取項目預設不關閉) |
className | string | — | 透傳到項目 |
Menubar.RadioGroup
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | string | — | 受控的選中值 |
defaultValue | string | — | 非受控模式的初始選中值 |
onValueChange | (value: string) => void | — | 選中值變動時回呼 |
className | string | — | 透傳到群組容器 |
Menubar.RadioItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | string | — | 這個選項代表的值(必填) |
onSelect | () => void | — | 選取時回呼 |
disabled | boolean | false | 停用 |
textValue | string | — | typeahead 比對用文字 |
closeOnSelect | boolean | false | 選取後是否關閉選單(單選項目預設不關閉) |
className | string | — | 透傳到項目 |
Menubar.Sub
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
open | boolean | — | 受控的子選單開啟狀態 |
defaultOpen | boolean | false | 非受控模式的初始開啟狀態 |
onOpenChange | (open: boolean) => void | — | 子選單開啟狀態變動時回呼 |
Menubar.SubTrigger
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
disabled | boolean | false | 停用 |
inset | boolean | false | 左側留出與核取項目對齊的 icon 空間 |
textValue | string | — | typeahead 比對用文字 |
className | string | — | 透傳到項目 |
Menubar.SubContent
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
sideOffset | number | -4 | 與 SubTrigger 的距離(px),負值讓子選單稍微疊上父選單 |
alignOffset | number | -5 | 沿交叉軸的偏移(px),預設讓第一項與 SubTrigger 對齊 |
collisionPadding | number | 8 | 與視窗邊緣保持的最小距離(px) |
className | string | — | 透傳到子選單面板 |
Label/Group/Separator/Shortcut
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
Label.inset | boolean | false | 群組標題(非互動);inset 時與核取項目文字對齊 |
Group | — | — | 以 role="group" 包住一組項目 |
Separator | — | — | role="separator" 的細分隔線 |
Shortcut | — | — | 靠右的灰色等寬小字;只是標示,實際快捷鍵由你自己綁定 |
所有子元件都接受 className,以 cn() 併入。
細節
- 切換行為:沒有選單開著時,要點擊 Trigger 才開啟;只要有一個開著,指標移入其他 Trigger 就立即切換,沒有延遲。再點一次目前開著的 Trigger 會關閉。
- 只有一個面板容器:切換時不播放「舊面板退場+新面板進場」,而是同一個容器以彈簧(
switchStiffness/switchDamping)把位置與寬高從舊錨點補間到新 Trigger 下方,內容以duration交叉淡接。只有從全關到開啟、或開啟到全關時,才播放淡入+從 95% 放大+向下滑入 8px 的進出場。 - 定位:面板以
position: fixed掛到body,固定在 Trigger 下方靠左、間距 4px;下方放不下且上方空間較大時翻到上方,左右會推回視窗內並保留 8px。視窗縮放、任何祖先捲動、Trigger 或面板尺寸變化都以 requestAnimationFrame 節流重算,這類重算直接到位、不走彈簧,避免面板落後於錨點。 - 動畫時長同時寫入
--wb-menubar-duration,面板內所有 CSS 過渡都讀這個變數,可在全域覆寫。 - 子選單:停留
subDelay毫秒後展開,以 SubTrigger 為錨點在右側展開(放不下時翻到左側);指標斜向移往子選單時,以「離開點+子選單兩角」構成的安全三角形判斷,途中掠過其他項目不會誤關。 - typeahead:500ms 內連續輸入的字元累加成前綴比對
textValue(預設取項目文字、略過快捷鍵);中文以首字比對。 - 點擊選單列與所有面板之外會關閉選單,焦點交給被點的元素。
可及性
- 根元素為
role="menubar"、aria-orientation="horizontal";Trigger 為role="menuitem",帶aria-haspopup="menu"、aria-expanded與aria-controls - Trigger 之間採 roving tabindex:整條選單列只有一個 Tab 停駐點,←/→ 在 Trigger 間移動(
loop控制是否循環),Home/End 跳到頭尾 - 有選單開著時按 ←/→(焦點在面板內且不在 SubTrigger 上),會切換到相鄰選單並把焦點放進新選單第一項
- Trigger 上 ↓/Enter/Space 開啟並聚焦第一項、↑ 開啟並聚焦最後一項;面板內 ↑↓ 在非停用項間循環、Home/End 跳頭尾、Enter/Space 選取、Escape 關閉並回到 Trigger、Tab 關閉並讓焦點自然離開
- 面板為
role="menu"、aria-orientation="vertical",項目依類型使用menuitem、menuitemcheckbox、menuitemradio,核取與單選項帶aria-checked;停用項目帶aria-disabled並在巡覽時跳過;快捷鍵標示對輔助科技隱藏 - 子選單以 → 或 Enter 進入、← 或 Escape 返回 SubTrigger
- typeahead 與 Enter 在中文等輸入法組字中(
isComposing或 keyCode 229)不觸發 - 使用者系統開啟「減少動態效果」時,面板只保留淡入淡出(時長不變),不縮放、不位移;切換選單時面板直接跳到新位置,高亮列與單選圓點也直接到位