跳到主要內容

設計系統規格:零件庫與設計頁

tokens → primitives → components → patterns 的完整清單,元件的變體命名規則,以及設計頁必須展示什麼。

約 7 分鐘 · design-system.md

本文件為 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

  1. 這個值會在三個以上的地方出現嗎?不會 → 不要加,就地用既有 token 組合。
  2. 換風格時這個值需要跟著變嗎?不需要 → 它不是 token,是元件的實作細節。
  3. 它可以用既有 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-carddata-interactive / data-featured / data-flush.ds-panel + .ds-panel__body(內嵌深色面板,自帶網點) .ds-glass(玻璃層)

Data display

.ds-tabledata-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

.ds-tabs + .ds-tabs__tab .ds-accordion(建議直接用原生 <details name="x">

Overlay

.ds-scrim + .ds-dialogdata-size="lg".ds-dialog__head / .ds-dialog__foot

Layout primitives

.ds-containerdata-narrow)、.ds-sectiondata-band.ds-stack.ds-rowdata-wrap)、.ds-griddata-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 資料生成,不是手寫。手寫的設計頁三個月後一定是過期的。

必須展示:

  1. 色彩矩陣 — 依角色分組,每一格顯示變數名與實際值,透明色要用棋盤格底顯示真實 alpha
  2. 對比檢查表 — 至少七組,包含一組故意不合格的(accent 當文字),並標出 AAA / AA / AA-large / 不合格
  3. 字級 — 每一階的 token 名、用途、實際樣本
  4. 字體樣本 — display / body / mono 三種
  5. 間距 — 每一階畫成長條
  6. 圓角 — 每一階畫成方塊
  7. 陰影 — 三階
  8. 動態 — 三個時長可以滑過去比較
  9. 所有元件的所有變體,包含 disabled 與錯誤狀態
  10. 完整 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 可見
  • 原始碼裡沒有任何寫死的顏色、字體、間距

顯示設定

這裡改的每一項,會即時套用到站上所有預覽。

風格

密度

圓角

動態

系統層級的「減少動態效果」永遠優先於這裡的設定。

語言