設計系統規格:零件庫與設計頁
tokens → primitives → components → patterns 的完整清單,元件的變體命名規則,以及設計頁必須展示什麼。
本文件為 design.md 規格。風格決策見 style-neon-ink,落地計畫見 frontend-plan。
1. 系統總覽
tokens --ds-* 變數。唯一的設計決策來源。
↓
primitives 版面骨架:container / section / stack / row / grid
↓
components 可重複使用的零件:button / input / card / badge / table…
↓
patterns 多個元件組成的解法:篩選列+表格、空狀態、串流訊息…
↓
modules 完整可貼上的區塊:登入頁、定價表、錯誤頁…
每一層只能依賴它下面那一層。元件不可以知道模組的存在,token 不可以知道元件的存在。
2. Token 計畫
見 token-contract 的完整清單。這裡只講怎麼決定要不要加一個新 token:
- 這個值會在三個以上的地方出現嗎?不會 → 不要加,就地用既有 token 組合。
- 換風格時這個值需要跟著變嗎?不需要 → 它不是 token,是元件的實作細節。
- 它可以用既有 token 算出來嗎?可以 → 用
calc(),不要加新的。
新增 token 的成本是「每一個風格檔都要考慮要不要覆寫它」,所以門檻要高。
3. 元件命名規則
.ds-<block>__<element>
變體一律用 data 屬性,不是額外的 class:
<button class="ds-btn" data-variant="primary" data-size="lg" data-pill>
三個理由:
- 標記讀起來是「一個按鈕,主要的,大的」,不是「ds-btn ds-btn--primary ds-btn--lg ds-btn--pill」
- 轉換到 React / Vue 時,data 屬性原封不動,class 字串拼接則要重寫
- CSS 選擇器
[data-variant='primary']的權重跟 class 一樣,不會有覆寫順序的意外
4. 元件藍圖規則
UI 元件必須:
- 只接受 props,沒有 fetch、沒有 router、沒有 store
- 沒有 domain 知識(
<PricingCard>可以,<WhatSubProPlanCard>不可以) - 所有視覺值來自 token
- 每一個互動狀態都要設計:default / hover / active / focus-visible / disabled
5. 元件清單
Buttons
.ds-btn
- variant: primary / accent / secondary / ghost / danger
- size: sm / md / lg
- 修飾:
data-block(滿寬)、data-pill(膠囊) - 狀態: hover / active / disabled / focus-visible
Inputs
.ds-input .ds-textarea .ds-select .ds-field .ds-label .ds-hint .ds-error-text
.ds-check(勾選)、.ds-switch(開關,role="switch")、.ds-slider
.ds-input-group(輸入框 + 按鈕焊在一起)
Surfaces
.ds-card(data-interactive / data-featured / data-flush)
.ds-panel + .ds-panel__body(內嵌深色面板,自帶網點)
.ds-glass(玻璃層)
Data display
.ds-table(data-numeric 讓最後一欄靠右並用 tabular-nums)
.ds-table-scroll(寬表格一定要包這一層)
.ds-badge(tone: accent / soft / success / warning / danger / inverse)
.ds-chip、.ds-avatar、.ds-kbd、.ds-divider
Feedback
.ds-alert(tone: info / success / warning / danger)
.ds-toast、.ds-skeleton、.ds-progress、.ds-empty
Navigation
.ds-tabs + .ds-tabs__tab
.ds-accordion(建議直接用原生 <details name="x">)
Overlay
.ds-scrim + .ds-dialog(data-size="lg")
.ds-dialog__head / .ds-dialog__foot
Layout primitives
.ds-container(data-narrow)、.ds-section(data-band)
.ds-stack、.ds-row(data-wrap)、.ds-grid(data-cols)、.ds-spacer、.ds-sr
6. 設計 Patterns
| Pattern | 組成 | 關鍵細節 |
|---|---|---|
| 篩選列 + 表格 | input + chips + table + pager | 篩選狀態放 URL;選取列與篩選列同高,切換不位移 |
| 空狀態 | ds-empty | 分三種:還沒建立 / 搜尋無結果 / 沒有權限 |
| 載入骨架 | ds-skeleton | 尺寸必須等於真實內容,否則載完會跳 |
| 錯誤處理 | ds-alert + 錯誤頁 | 表單錯誤用一個 aria-live 區域,不要每欄一個 |
| 命令面板 | dialog + listbox | 焦點留在 input,用 aria-activedescendant 移動選取 |
| 設定頁 | rail + panel + save bar | 主要動作在沒有變更時 disabled |
| 串流訊息 | 見 llm-streaming | 游標接在最後一個文字節點後面 |
7. 設計頁規格(強制存在)
每一個風格都必須有一頁 /styles/<id>/design,而且內容從 token 資料生成,不是手寫。手寫的設計頁三個月後一定是過期的。
必須展示:
- 色彩矩陣 — 依角色分組,每一格顯示變數名與實際值,透明色要用棋盤格底顯示真實 alpha
- 對比檢查表 — 至少七組,包含一組故意不合格的(accent 當文字),並標出 AAA / AA / AA-large / 不合格
- 字級 — 每一階的 token 名、用途、實際樣本
- 字體樣本 — display / body / mono 三種
- 間距 — 每一階畫成長條
- 圓角 — 每一階畫成方塊
- 陰影 — 三階
- 動態 — 三個時長可以滑過去比較
- 所有元件的所有變體,包含 disabled 與錯誤狀態
- 完整 token 原始碼,可一鍵複製成
tokens.css
8. 無障礙基準
- 所有互動元素必須 focus 可見(統一由
--ds-ring提供),不可以outline: none而不補 - 顏色不可以是唯一的資訊管道 —— 狀態要有文字或圖形
- 主文字對比 ≥ 4.5:1,大字與 UI 邊界 ≥ 3:1
- 彈窗用原生
<dialog>+showModal(),自動得到焦點鎖定與 Escape - 摺疊用原生
<details> - 圖示按鈕必須有
aria-label - 動態一律尊重
prefers-reduced-motion
詳見 accessibility。
9. 完成的定義
一個元件做完,代表:
- 所有變體都在設計頁上看得到
- 每個互動狀態都設計過(含 disabled 與錯誤)
- 在六個風格下都檢查過(特別是深色的 Midnight Glass 和零圓角的 Brutal Mono)
- 鍵盤可操作,focus 可見
- 原始碼裡沒有任何寫死的顏色、字體、間距