導航
玻璃底欄
手機底部的玻璃質感分頁列 React 元件:圖示掛載時依序彈出、選取底塊在分頁間滑動、選中項標籤展開,可選中央凸起主按鈕與未讀徽章數字。
手機 App 底部的玻璃質感分頁列。掛載時每個圖示依序以帶點回彈的彈簧彈出;切換分頁時,白色選取底塊從上一項滑到新的一項,選中項同時由「僅圖示」展開成「圖示 + 標籤」。項目數為奇數時可開啟 raisedCenter,把中間那項改成凸起的圓形主按鈕(例如「新增」);徽章支援未讀數字(超過 99 顯示 99+)與小紅點,數字變化時會縮放提醒一次。選中值支援受控/非受控雙模式。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add glass-tab-barPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
18
60
編輯中的呼叫預設屬性
<GlassTabBar/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/glass-tab-bar.json或在 components.json 設定 registries 後,改用 @webberui/glass-tab-bar 安裝。
使用
import { Compass, House, MessageCircle, Plus, User } from "lucide-react";
import { GlassTabBar } from "@/components/ui/glass-tab-bar";
const [tab, setTab] = React.useState("home");
<GlassTabBar
position="fixed"
raisedCenter
value={tab}
onValueChange={setTab}
onCenterPress={() => openComposer()}
items={[
{ id: "home", icon: <House />, label: "首頁" },
{ id: "explore", icon: <Compass />, label: "探索" },
{ id: "create", icon: <Plus />, label: "新增" },
{ id: "messages", icon: <MessageCircle />, label: "訊息", badge: 3 },
{ id: "me", icon: <User />, label: "我的" },
]}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | GlassTabBarItem[] | — | 分頁項目清單 |
value | string | — | 受控的選中項目 id;未提供時由元件內部管理(非受控) |
defaultValue | string | 第一項的 id | 非受控模式的初始選中 id |
onValueChange | (value: string) => void | — | 選中項目改變時回呼,參數為新的 id |
raisedCenter | boolean | false | 項目數為奇數(至少 3 項)時,把中間那項改成凸起的圓形主按鈕 |
onCenterPress | () => void | — | 點擊中央主按鈕時回呼 |
centerSelectable | boolean | false | 中央主按鈕是否也會改變 value;預設只觸發 onCenterPress |
blur | number | 18 | 玻璃背景模糊程度(px);未提供時使用 --wb-gtb-blur |
stagger | number | 60 | 掛載時圖示依序彈出的間隔(ms);未提供時讀取 --wb-gtb-stagger |
position | "fixed" | "static" | "static" | fixed 固定在視窗底部並留 safe-area;static 依文件流排版 |
ariaLabel | string | "主要導覽" | nav 的無障礙名稱 |
className | string | — | 透傳到最外層 nav |
GlassTabBarItem
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 唯一識別碼,也是 value 的值 |
icon | React.ReactNode | — | 分頁圖示,建議 20px 的 lucide 圖示 |
label | string | — | 分頁名稱;選中時顯示在圖示旁,未選中時作為無障礙名稱 |
badge | number | boolean | — | 數字顯示未讀數(超過 99 顯示 99+),true 顯示小紅點 |
CSS 變數
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
--wb-gtb-blur | length | 18px | 玻璃背景模糊程度 |
--wb-gtb-stagger | time | 60ms | 圖示依序彈出的間隔(stagger prop 優先) |
--wb-gtb-highlight | color | rgba(255,255,255,0.6) | 頂緣 1px 內高光 |
--wb-gtb-accent | color | #171717(dark:#f5f5f5) | 中央主按鈕底色 |
--wb-gtb-badge | color | #ef4444 | 徽章底色 |
細節
- 玻璃材質:半透明白底(dark 為 neutral-900/55)加
backdrop-blur與backdrop-saturate-150,頂緣一道 1px 內高光、外框 1px 淡邊與柔和外陰影,整體為圓角膠囊。 - 選取底塊用 motion 的
layoutId在分頁間滑動;layoutId以useId加前綴,同一頁放多個底欄也不會互相干擾。 - 選中項的寬度以 layout 動畫展開,標籤延遲 60ms 淡入並從左側滑入 4px;首次渲染直接顯示,不播放展開動畫。
- 中央主按鈕直徑 56px、上浮 18px,不參與底塊滑動;它不是 tab,點擊只呼叫
onCenterPress,除非開啟centerSelectable。 - 掛載彈出等元件掛載後才開始,這時已確定使用者的動態偏好與 CSS 變數值,SSR 首次渲染與 client 一致。
可及性
- 外層為
nav(預設aria-label「主要導覽」),內部為role="tablist",每個分頁是role="tab"並設定aria-selected - 分頁的
aria-label為其標籤,有徽章時附上「,3 則未讀」或「,有新通知」;視覺上的徽章與展開標籤對輔助科技隱藏,避免重複朗讀 - 採 roving tabindex:只有選中的分頁在 Tab 順序中,←/→ 循環移動、Home/End 跳到首尾,移動同時選取(自動啟用)
- 中央主按鈕是一般
button(不屬於 tablist),以項目標籤作為aria-label;開啟centerSelectable且選中時設定aria-current="page" - 所有按鈕皆為
type="button"並有 focus-visible 外框 - 使用者系統開啟「減少動態效果」時,圖示不彈出、底塊直接跳到位、徽章不縮放、標籤直接顯示