Button 按鈕
觸發一個動作。
在 Storybook 開啟何時不要用
✅ 這樣做
導向另一個頁面 → 用連結(<a>)。需要長得像按鈕就用 asChild。
🚫 不要這樣
不要用 <button onClick={() => navigate()}> 做導覽——
使用者沒辦法中鍵開新分頁、沒辦法複製網址,而且搜尋引擎與螢幕報讀器都讀不懂。
變體
default 主要動作 · secondary 次要 · outline 外框 · ghost 淡化 · link 連結樣式 · destructive 破壞性動作
變體與尺寸
狀態
| 狀態 | 表現 |
|---|---|
| 預設 | 依變體 |
| hover | 狀態層 6%(疊一層 currentColor,不換底色) |
| pressed | 狀態層 14%——按住的瞬間要有回饋 |
| focus-visible | 2px 聚焦環 + 2px 偏移(只在鍵盤操作時出現) |
| disabled | 透明度 50%、pointer-events: none,且不套狀態層 |
| loading | 沒有獨立變體:設 disabled、換旋轉圖示、改文案 |
hover 與 pressed 由 .state-layer 這個 utility 提供,六個變體共用同一組強度。
改版前每個變體各挑一個透明度(/90、/80…),可見度從 ΔE00 0.6
(secondary,等於沒有 hover)到 7.4(default,過於刻意)差了十倍;
現在全部落在 2.6–4.5。理由與量測見互動狀態的顏色邏輯。
link 變體刻意不套狀態層:它是一段文字不是一塊表面,疊上去會在字後面浮出一個色塊。
它的 hover 走底線。
為什麼沒有 loading 變體:多一個變體只會讓人猶豫該用哪個。 用既有的 disabled + 圖示 + 文案,語意更清楚(「正在處理,現在不能按」)。 這是載入中三種手段的第三種—— 內容區域的載入(骨架、就地變暗)是另外兩種,不要拿按鈕的做法去蓋整個畫面。
一顆畫面上只有一顆主要按鈕
default 變體是「這個畫面最主要的那件事」。兩顆並排的深色按鈕等於沒有主次。
無障礙
- 圖示按鈕必須有
aria-label - 破壞性動作建議搭配 Dialog 二次確認
- 觸控裝置的按鈕至少 44px(
size="default"已達 40px,密集區域加tap-target)
取用
npx shadcn@latest add https://kielchang.github.io/dooping-design-book/r/button.json