WebberUI

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.jsonfiles[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="主導覽" />;
}

受控模式

傳入 selectedonChange 即進入受控模式,可與其他狀態同步:

const [selected, setSelected] = React.useState<number | null>(0);

<ExpandableTabs tabs={tabs} selected={selected} onChange={setSelected} />;

Props

Prop型別預設值說明
tabsExpandableTabItem[]分頁清單,可混入 { type: "separator" } 分隔線
selectednumber | null受控選中索引,null 表示全部收合
defaultSelectednumber | nullnull非受控模式的初始選中索引
onChange(index: number | null) => void選中變更時觸發
collapsiblebooleantrue再次點擊當前選中的分頁是否收合
deselectOnOutsideClickbooleantrue點擊元件外部時是否收合
labelstring無障礙群組標籤(role="toolbar"
activeClassNamestring選中分頁的強調色 class(文字/圖示)
classNamestring附加到容器的 class

其中 ExpandableTabItem{ title: string; icon: React.ReactNode }{ type: "separator" }

細節

  • 選中的分頁以 spring 轉場同步展開容器內距與標題寬度,收合時反向 morph
  • 圖示以 React.ReactNode 傳入,可搭配任意圖示庫;SVG 會自動限制為 size-5
  • 同時只有一顆分頁可被 null 選中,未選中時全部收合為純圖示

可及性

  • 容器為 role="toolbar",可透過 label 提供 aria-label
  • 每顆分頁皆有 aria-labelaria-pressed,展開文字時仍保有標籤
  • 方向鍵(←/→/↑/↓)與 Home/End 於分頁間移動焦點(roving tabindex),自動跳過分隔線
  • 使用者系統開啟「減少動態效果」時,展開/收合改為即時切換,不播放位移動畫

On this page