Expandable Tabs
圖示式分頁導覽,選中時圖示平滑展開為文字,支援分隔線與受控/非受控模式。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
<ExpandableTabs />
安裝
npx shadcn@latest add https://webberui.com/r/expandable-tabs.json或在 components.json 設定 registries 後,改用 @webberui/expandable-tabs 安裝。
安裝依賴後,從 registry JSON(/r/expandable-tabs.json 的 files[0].content)複製 expandable-tabs.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge圖示可自由選用,本範例使用 lucide-react。
使用
import { Bell, Home, Settings, User } from "lucide-react";
import {
ExpandableTabs,
type ExpandableTabItem,
} from "@/components/ui/expandable-tabs";
const tabs: ExpandableTabItem[] = [
{ title: "首頁", icon: <Home /> },
{ title: "通知", icon: <Bell /> },
{ type: "separator" },
{ title: "設定", icon: <Settings /> },
{ title: "個人", icon: <User /> },
];
export function Nav() {
return <ExpandableTabs tabs={tabs} label="主導覽" />;
}受控模式
傳入 selected 與 onChange 即進入受控模式,可與其他狀態同步:
const [selected, setSelected] = React.useState<number | null>(0);
<ExpandableTabs tabs={tabs} selected={selected} onChange={setSelected} />;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
tabs | ExpandableTabItem[] | — | 分頁清單,可混入 { type: "separator" } 分隔線 |
selected | number | null | — | 受控選中索引,null 表示全部收合 |
defaultSelected | number | null | null | 非受控模式的初始選中索引 |
onChange | (index: number | null) => void | — | 選中變更時觸發 |
collapsible | boolean | true | 再次點擊當前選中的分頁是否收合 |
deselectOnOutsideClick | boolean | true | 點擊元件外部時是否收合 |
label | string | — | 無障礙群組標籤(role="toolbar") |
activeClassName | string | — | 選中分頁的強調色 class(文字/圖示) |
className | string | — | 附加到容器的 class |
其中 ExpandableTabItem 為 { title: string; icon: React.ReactNode } 或 { type: "separator" }。
細節
- 選中的分頁以 spring 轉場同步展開容器內距與標題寬度,收合時反向 morph
- 圖示以
React.ReactNode傳入,可搭配任意圖示庫;SVG 會自動限制為size-5 - 同時只有一顆分頁可被
null選中,未選中時全部收合為純圖示
可及性
- 容器為
role="toolbar",可透過label提供aria-label - 每顆分頁皆有
aria-label與aria-pressed,展開文字時仍保有標籤 - 方向鍵(←/→/↑/↓)與 Home/End 於分頁間移動焦點(roving tabindex),自動跳過分隔線
- 使用者系統開啟「減少動態效果」時,展開/收合改為即時切換,不播放位移動畫