Interaction States
pressed ≠ selected
互動狀態分兩層,不可合併:
| 層 | State | 性質 | 怎麼觸發 |
|---|---|---|---|
| Interaction 短暫、使用者操作中 | hover | 指標移入 | hover: |
| pressed | 手指 / 滑鼠正在按下 | pressed:(React Aria data-pressed) | |
| focus | 鍵盤焦點 | focus-visible:(目前全站無 focus ring,重新引入需對照線上並拍板) | |
| disabled / loading | 由 prop 關閉 / 等待中 | disabled: / data-loading: | |
| Selection 持久、由邏輯控制 | selected | 目前被選中的那一個 | tabs、radio、checkbox、menu item、outcome 按鈕 |
| on / off | 開關 | toggle、switch |
「active」禁止當 state 名稱。這個詞在 CSS 是 :active、在 React Aria 是 pressed、在口語是「目前選中」, 三種意思撞在一起。遇到就改成 pressed 或 selected。@olab/components Button 的 active prop 是 production 既有命名,語意是 selected,不在此規則內。
示意:selected 是持久的,pressed 只在按下的瞬間
按住不放 = pressed;放開後留下的 = selected
Pressed 一定要看得到
- 每一個互動元件在按下的瞬間都要有即時回饋,light / dark 都要看得出來。
- pressed 走 interaction layer(
pressed:variant),禁止存進 state、禁止用isPressed這類 prop 對外控制、禁止當持久狀態用。 - Live DS 的 family 色只有「淡底 + 一個 solid」兩階,沒有更深的 pressed 階,所以 pressed 與 selected+hover 用 brightness 做回饋(
brightness-90/brightness-110)。 - 新增或修改互動元件時,preview 頁的 Interactive Demo 必須能按出 pressed,否則不算完成。
手機沒有 hover
觸控螢幕上 hover 不存在。桌機靠 hover 才看得出「這個可以點」的設計,到手機上會變成死的。
- 靜止狀態就要看得出可點:品牌色、常駐底線、按鈕外觀,不能把可點性藏在 hover 裡。
- 以 pressed 回饋取代 hover 回饋——這也是上一節 pressed 必須可見的原因之一。
- web 上寫的 DS hover 規則套到手機之前,先檢查沒有 hover 時這個元件還成不成立。
禁止事項
- 自訂 hover 顏色、pressed / selected 樣式,或用 className 覆寫 component 的 interaction。
- 用
div/span+onClick模擬按鈕——一律用 Button / ButtonUtility。 - 在 React Aria 元件上用 CSS
active:(它不觸發:active)——用pressed:。 - 手動控制 focus ring。
- disabled 用 family 色降不透明度——走
disabled:opacity-50的中性做法。