Surfaces - 表面層級系統 是做什麼的?
此技能決定每個元件由什麼構成:填充色、邊緣、陰影、圓角、狀態與圖示。它從一個簡短的配方清單中挑選,確保兩個畫面從不產生兩種不同的卡片。
在做出任何決定之前,請先套用 product-ui 的 §1(品味日誌與優先級)與 §5(各技能職責歸屬)。
按此順序決定
- 是否需要表面? 先移除再新增。有周圍空間的標題不需要卡片。切勿將卡片嵌套在另一張卡片內。原因:每個容器都增加一條必須被眼睛閱讀的邊緣。
- 哪個層級? 頁面、隆起、浮動或覆蓋。每個元素一個層級,根據其功能而非你想要它多突出來選擇。elevation.md
- 什麼繪製邊緣? 高程使用陰影環。結構使用邊框。同一邊緣永不併用。elevation.md
- 哪個圓角? 根據元素角色取得代幣,然後讓嵌套圓角同心。elevation.md
- 哪些狀態? 在打磨預設狀態之前,先設計懸停、按下、選中、禁用和焦點可見作為表面的變化。states-and-icons.md
- 哪種圖示處理? 一組圖示、
currentColor、筆畫粗細與文字匹配。states-and-icons.md - 是否允許材質? 僅限浮動在滾動內容上的 chrome 效果。materials-and-edges.md
- 滾動邊緣。 分隔線、淡出效果、過度滾動。materials-and-edges.md
四個層級
| 層級 | 適用時機 | 淺色模式 | 深色模式 |
|---|---|---|---|
| 頁面 (Page) | 背景基底 | 底色填充,無陰影 | 最深填充,無陰影 |
| 隆起 (Raised) | 卡片、控制項、選中選項 | 白色,環 + 1px 陰影 + 3px 緊密陰影 | 亮一階,白色環,黑色緊密陰影 |
| 浮動 (Floating) | 選單、彈出視窗、工具提示、提示訊息 | 白色,環 + 1px 陰影 + 4px 緊密陰影 | 亮兩階,白色環,黑色緊密陰影 |
| 覆蓋 (Overlay) | 對話框、側邊欄、命令面板(搭配遮罩) | 白色,環 + 1px 陰影 + 6px 緊密陰影 | 亮三階,白色環,黑色緊密陰影 |
確切代幣請見 elevation.md。背後的規則:
- 環加緊密陰影。 模糊 ≤ 8px,偏移 ≤ 6px,陰影帶負擴散。原因:大面積柔和模糊看起來像發光,使頁面混濁且顯得刻板。
- 深色模式透過變亮來提升高度。 陰影在深色背景下幾乎不可見,因此亮度承載高度,低透明度的白色環承載邊緣。
- 陰影是半透明且單一顏色的。 不透明的灰色會留下光暈。深色陰影保持黑色。
- 同一層級的兄弟元件共享設定。 一行中的卡片共用一個陰影、一個圓角、一個內距。
- 懸停改變填充色,而非陰影。
無需查閱參考文件的規則
- 邊框顯示結構,陰影顯示高度。 分隔線、輸入框、表格儲存格和選中狀態使用邊框。僅為了提升高度而存在的邊框改用環。
- 在陰影環下保留
border: 1px solid transparent。 原因:強制色彩模式會移除陰影,而邊框仍能繪製邊緣。 - 圖片添加 1px 內嵌輪廓:
outline: 1px solid黑色 10% 透明度(深色模式白色 10% 透明度),outline-offset: -1px。絕不使用色調顏色(會產生邊緣毛邊)。跳過 Logo 與透明藝術圖。 - 同心圓角。 外圓角 = 內圓角 + 內距(+ 邊框),最多到 24px 內距。超過該值則視為獨立表面。
- 焦點永遠是環,不能是無。
:focus-visible顯示 2px 強調色輪廓,偏移 2px;輸入框則使用無間隙的 2px 強調色邊緣。outline: none而沒有替代方案視為錯誤。環不佔用佈局空間。 - 禁用控制項不接受懸停、按下或縮放。
- 圖示:每個表面一組圖示、
currentColor,預設為空心,啟用時為實心。筆畫粗細與相鄰文字粗細匹配,400 字重時 1.5px,600 字重時 2px。 - 背後模糊僅在浮動 chrome 效果上使用。 絕不用於內容、卡片或表格。
- 可滾動覆蓋層(對話框、抽屜、選單)需設定
overscroll-behavior: contain。
錯誤修正對照表
| 錯誤寫法 | 修正寫法 | 原因 |
|---|---|---|
卡片使用 border: 1px solid #e5e5e5 | box-shadow: var(--elev-raised); border: 1px solid transparent | 純深度邊框改為環;強制色彩模式仍可繪製邊緣 |
box-shadow: 0 10px 40px rgba(0,0,0,.2) | 0 0 0 1px a.08, 0 1px 2px a.12, 0 4px 8px -4px a.16 | 緊密清晰勝過巨大柔和 |
深色卡片使用 0 8px 24px #000 | 更亮的填充 + 0 0 0 1px oklch(1 0 0 / .08) | 陰影在深色中消失;亮度顯示高度 |
| 卡片圓角 8,內部圖片圓角 8,內距 8 | 內部 8,外部 16 | 同心圓角保持均勻間距 |
<img> 融入白色卡片 | outline: 1px solid oklch(0 0 0 / .1); outline-offset: -1px | 無需邊框即可看出邊緣 |
button:focus { outline: none } | button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px } | 鍵盤使用者保留定位 |
| 1.5px 圖示筆畫搭配 600 粗體標籤 | 2px 筆畫 | 筆畫粗細與文字主幹粗細匹配 |
| 工具列每個按鈕都使用實心圖示 | 預設空心,啟用時實心 | 填充是狀態信號 |
卡片使用 backdrop-filter: blur(20px) | 不透明表面 | 模糊僅適用於浮動在滾動內容上的 chrome |
完成前檢查清單
| 模式 | 修正方式 |
|---|---|
box-shadow 模糊值 > 8px 或 alpha > .2(淺色 UI) | 使用層級代幣 |
border 與 box-shadow 做相同工作 | 僅保留環與透明邊框 |
陰影環缺少 border: 1px solid transparent | 補上 |
父層與其內含內距子層使用相同 border-radius | 父層使用 inner + padding |
outline: none 或 outline: 0 但未以 :focus-visible 取代 | 加上環 |
使用 :focus(而非 :focus-visible)設定環 | 改為 :focus-visible |
狀態改變 border-width | 改用顏色、填充或等寬的透明邊框 |
圖示內硬編碼 fill="#…" 或 stroke="#…" | 使用 currentColor |
| 同一畫面載入兩套圖示庫 | 只留一套 |
backdrop-filter 用於非 sticky 欄、側邊欄或浮動選單之外 | 移除 |
覆蓋層清單未設定 overscroll-behavior: contain | 補上 |
滾動器邊緣硬切,或因 overflow: hidden 裁剪陰影 | 淡出邊緣;為滾動器添加內距 |
驗證方式:以 2 倍率渲染淺色與深色模式,並裁剪每個層級的邊緣。對每個控制項執行懸停、按下、Tab 聚焦、選中與禁用,比較前後尺寸。切換強制色彩模式(Chrome 開發者工具 → 渲染模擬),確認每張卡片仍有邊緣。