PC01▸🧭 Navigation · 導航▸頁籤
Tabs
頁籤
難度 ★★★★★標籤 tabs · indicator · 鍵盤3 min read
模式簡述
Tabs:平行內容切換。indicator 動畫滑過去比直接跳更高級。鍵盤要支援左右箭頭。
當前面板:概覽 的內容
Claude Code Prompt
參考:PC01 tabs 組件:Tabs.tsx Tabs:平行內容切換。indicator 動畫滑過去比直接跳更高級。鍵盤要支援左右箭頭。 限制: - indicator 直接跳不動畫 - ARIA 屬性沒寫對 - 重置 tab 內 state 沒處理
完整程式碼
"use client";
import { useEffect, useState } from "react";
import { motion } from "framer-motion";
type State = "first" | "second" | "third";
const TABS = ["概覽", "設定", "整合"];
export function Tabs({ state = "first" }: { state?: State }) {
const idx = state === "first" ? 0 : state === "second" ? 1 : 2;
const [active, setActive] = useState(idx);
useEffect(() => setActive(idx), [idx]);
return (
<div className="w-full max-w-md">
<div className="flex gap-1 border-b border-border">
{TABS.map((t, i) => (
<button
key={t}
onClick={() => setActive(i)}
className="relative px-4 py-2 text-sm transition"
>
<span className={i === active ? "text-foreground" : "text-muted-foreground"}>{t}</span>
{i === active && (
<motion.div
layoutId="tab-indicator"
className="absolute inset-x-0 -bottom-px h-0.5 bg-lab-accent"
transition={{ type: "spring", stiffness: 400, damping: 30 }}
/>
)}
</button>
))}
</div>
<div
className="mt-5 rounded-md border border-border bg-background/30 p-4 text-sm text-muted-foreground"
style={{ fontFamily: "var(--font-zh)" }}
>
當前面板:<span className="text-foreground">{TABS[active]}</span> 的內容
</div>
</div>
);
}何時用
- ✅ 設定頁的不同子區塊
- ✅ 資料的不同視角(日/週/月)
- ✅ 詳情頁的子內容分頁
何時別用
- ❌ 分頁是順序流程(用 stepper)
- ❌ 分頁超過 5 個(用 dropdown 或 sidebar)
- ❌ 每個 tab 內容是不同 URL(用一般 nav)
我踩過的坑
-
indicator 直接跳不動畫:用 Framer layoutId 滑過去,瞬間升級。
-
ARIA 屬性沒寫對:role='tablist' tab + 'tabpanel' + aria-selected。鍵盤要 ArrowLeft/Right。
-
重置 tab 內 state 沒處理:使用者切回來上次 scroll 位置沒了。需要時用 keep-mounted。
相關模式
- PE08
- PC04