設計觀點
為什麼設計系統是產品成功的基石
在現代產品開發中,設計系統已經從「加分項目」變成了「必需品」。一套好的設計系統不僅能加速開發流程,更能確保使用者在不同平台上獲得一致的體驗。本文將探討設計系統的核心價值,以及如何從零開始建立屬於你的設計語言。
TL;DR
什麼是設計系統?
設計系統(Design System)是一套完整的設計標準集合,包含了可重複使用的元件、設計 Token、以及使用指南。它是設計師與開發者之間的共同語言,確保團隊中的每個人都在同一個基礎上工作。
不同於 UI Kit 或元件庫,設計系統更加全面。它不僅定義了視覺元素的外觀,還規範了它們的行為、使用情境與無障礙性要求。從字型大小到按鈕的互動狀態,每一個細節都有明確的規範。
「設計系統不是一個專案,而是一個產品。它需要持續維護、迭代與更新,才能真正發揮價值。」
— Brad Frost, Atomic Design 作者
設計系統的核心組成
一個成熟的設計系統通常包含以下幾個層面,從最基礎的 Token 到高階的頁面模板:
- 設計 Token — 色彩、字型、間距、圓角等最基礎的設計決策,通常以變數的形式存在。
- 基礎元件 — 按鈕、輸入框、標籤等最小的 UI 構建塊,每個元件都有明確的 API 與使用規範。
- 複合元件 — 由多個基礎元件組合而成的模組,例如表單、卡片、導覽列等。
- 頁面模板 — 定義了常見頁面的佈局結構,提供一致的使用者體驗框架。
CJK 排版的挑戰
對於繁體中文產品來說,設計系統需要額外關注排版(Typography)的細節。CJK 字元的結構與 Latin 字母截然不同,這帶來了幾個獨特的挑戰:
- CJK 字元每個字寬度相同(等寬字元),而 Latin 字母則是比例字元,兩者混合排列時需要特別處理間距
- 繁體中文筆畫較多,在小尺寸下容易糊成一團,因此最小字級不宜低於 12px
- 行高需要比英文更大——繁體中文建議使用 1.75 而非英文常用的 1.5
- 標點符號的位置與間距需要符合中文排版慣例,例如全形標點不應出現在行首
關於 Perfect Fourth 比例
Brickverse 的字級系統採用 Perfect Fourth (1.333) 比例,相較於常見的 Major Third (1.250), 在中文方塊字環境下能提供更強烈的層級對比。基準字級 16px 乘以 1.333 得到 21px、28px、38px 等字級, 每一級之間的跳躍在視覺上都能清楚感知。
字級比例對照表
以下為 Brickverse 設計系統中各個字級 Token 的具體數值與對應的使用場景:
| Token | 大小 | 使用場景 | 字重 |
|---|---|---|---|
| text-4xl | 38px | 頁面主標題 (h1) | Bold 700 |
| text-3xl | 28px | 段落大標題 (h2) | Bold 700 |
| text-2xl | 24px | 段落子標題 (h3) | SemiBold 600 |
| text-xl | 21px | 小標題 (h4) | Medium 500 |
| text-lg | 18px | 引言段落、h5 | Medium 500 |
| text-base | 16px | 內文 | Regular 400 |
| text-sm | 14px | 輔助文字、標籤 | Regular/Medium |
| text-xs | 12px | 圖說、最小文字 | Regular 400 |
建立設計系統的步驟
從我們在 Brickverse 的實踐經驗出發,我們將建立設計系統的過程拆解為五個階段。每個階段都有明確的目標與交付物,確保團隊能夠循序漸進地推動這項工作。
第一階段:設計稽核
在開始建立設計系統之前,首先需要對現有產品進行全面的設計稽核。這個步驟的目的是盤點目前已經存在的設計元素,找出不一致的地方,並確定優先需要標準化的項目。
- 視覺元素盤點
- 色彩:記錄所有使用中的色彩值,找出相似但不同的色碼
- 字型:統計不同頁面使用的字級、字重組合
- 間距:檢查 padding、margin 是否有一致的規律
- 元件盤點
- 按鈕:找出所有不同樣式的按鈕,歸納出核心變體
- 表單控制項:輸入框、下拉選單、勾選框等
- 導覽元件:頂部導覽、側邊欄、麵包屑等
- 互動模式盤點
- 動態效果:hover、focus、active 狀態的處理方式
- 過渡動畫:元素出現、消失、狀態切換的動畫時長與曲線
第二階段:建立 Token 系統
Token 是設計系統的原子級基礎。它們是以變數形式存在的設計決策,涵蓋色彩、字型、間距、圓角、陰影等面向。Brickverse 使用 TypeScript 定義 Token,搭配 Tailwind CSS v4 的 @theme inline 實現 CSS 自訂屬性的自動生成。
// src/tokens/typography.ts
export const fontSizes = {
xs: { value: '0.75rem', px: 12 },
sm: { value: '0.875rem', px: 14 },
base: { value: '1rem', px: 16 },
lg: { value: '1.125rem', px: 18 },
xl: { value: '1.3125rem', px: 21 },
'2xl': { value: '1.5rem', px: 24 },
'3xl': { value: '1.75rem', px: 28 },
'4xl': { value: '2.375rem', px: 38 },
} as const;第三階段:元件開發
基於 Token 系統,開始開發最常用的基礎元件。每個元件都應該具備完整的變體(variant)、尺寸(size)、狀態(state)定義,並附帶使用文件。
注意:中文字重的限制
在 Windows 10 以後的系統上,字重低於 300 會出現渲染模糊的問題。因此 Brickverse 設計系統建議最低字重為 Regular (400), Light (300) 僅在特殊裝飾性場景中使用。這是中文字型特有的考量——中文筆畫密度高,過細的字重會嚴重影響可讀性。
第四階段:文件與治理
設計系統的長期成功取決於完善的文件和治理機制。文件不只是給設計師看的——開發者、PM、QA 都需要理解設計決策背後的邏輯。
#0184ff 或一個間距值 16px。clamp() 函數讓字級在不同螢幕寬度之間平滑縮放,避免在斷點處產生突兀的跳變。第五階段:持續迭代
設計系統不是一個「做完就結束」的專案,而是一個持續演進的產品。團隊需要建立定期回顧的機制,根據實際使用反饋不斷調整和優化。
收集回饋
定期向使用設計系統的設計師和開發者收集意見,了解哪些元件好用、哪些需要改進。
分析數據
追蹤元件的使用頻率和覆蓋率,找出最常被客製化覆寫的元件——這通常意味著 API 設計不夠靈活。
版本規劃
使用語意化版本號(SemVer),明確區分 patch、minor、major 更新,讓使用者能安全地升級。
發布與溝通
每次更新都附帶完整的 changelog,並在團隊內部進行溝通,確保所有人了解變更內容。
實際案例:Brickverse 的排版決策
以下是我們在建立 Brickverse 字型系統時做出的幾個關鍵決策,以及背後的思考過程。這些經驗對於任何需要處理中文排版的團隊都有參考價值。
為什麼選擇 Noto Sans TC?
我們在字型選擇上評估了多個方案,最終選定 Noto Sans TC 作為主要字型。決策因素包括:
Noto Sans TC 的優勢
- - 免費開源,無授權費用問題
- - Google Fonts 直接載入,CDN 快速
- - 覆蓋完整的繁體中文字集
- - 提供 7 種字重 (100-900)
- - 內建中英文字距微調
需要注意的地方
- - 中文字體檔案較大(需做子集化)
- - 某些罕用字可能缺字
- - 極小字級 (10px) 下渲染略粗
- - 與其他 CJK 字體混排需校準
響應式字級的實作
我們使用 CSS clamp() 函數實現 Fluid Typography。以 h1 為例,它在 640px 的手機螢幕上渲染為 28px, 隨著螢幕增大平滑縮放,最終在 1280px 以上的桌面螢幕上達到 38px。中間任何寬度都會自動插值,不需要手動設定斷點。
/* globals.css — h1 的 fluid typography */
h1 {
font-weight: 700;
letter-spacing: 0em;
line-height: 1.4;
font-size: clamp(1.75rem, 1.25rem + 1.25vw, 2.375rem);
/* 最小值28px ↑ 線性縮放公式 最大值38px */
}
/* body 不做 fluid,保持固定 16px */
body {
font-size: 1rem;
line-height: 1.75;
letter-spacing: 0em;
}程式碼與行內標記
在技術文件中,經常需要在文字中穿插程式碼片段。行內程式碼使用 <code> 標籤,會自動套用 font-mono 字型、text-sm 字級和淺灰背景。例如:使用 var(--text-4xl) 來引用 38px 的字級變數, 或者透過 className="text-4xl font-bold" 來直接套用 Tailwind 的類別。
設計系統的投資報酬率
根據多項業界研究,導入設計系統後的產品團隊在效率和品質上都有顯著的提升:
47%
開發速度提升
33%
設計一致性改善
60%
元件重複使用率
25%
維護成本降低
關於數據的說明
上述數據來自 Forrester Research 對大型企業設計系統的調查報告。實際效果因團隊規模、產品複雜度和導入深度而異。 小型團隊的投資報酬期可能更短,但也需要根據實際情境評估是否適合建立完整的設計系統。
「好的排版是隱形的——讀者應該專注於內容本身,而非字型的存在。當你注意到排版的時候,通常代表它出了問題。」
— Oliver Reichenstein, iA Writer 創辦人
Markdown 渲染情境案例
在 CJK(中日韓)排版環境下,Markdown 渲染引擎(如 marked.js)會遇到一些 Latin 語系不會出現的 edge case。以下記錄了我們在 Brickverse Blog 實際遇到的情境與解法,作為設計系統的排版筆記。
情境 1:粗體後緊接中文字元
CommonMark 規範要求右側 flanking delimiter(**)後面不能緊接字母或 CJK 字元,否則不被視為粗體結束標記。這導致以下 Markdown 在預設 marked.js 中無法正確渲染:
**CDP 是 Chrome DevTools Protocol 的縮寫。**如果你用過 Chrome 開發者工具...
// marked.js 預設輸出:
// <p>**CDP 是 Chrome DevTools Protocol 的縮寫。**如果你用過...</p>
// ↑ 星號原封不動輸出,沒有轉成 <strong>解法:自訂 marked.js 的 emStrong tokenizer,放寬右側 delimiter 的規則,讓 **text** 後面即使緊接 CJK 字元也能正確關閉。
import { marked, type TokenizerObject } from 'marked';
const tokenizer: TokenizerObject = {
emStrong(src: string) {
// Bold: 更寬鬆的匹配,不要求 ** 後面是空格
const boldMatch = src.match(/^\*\*([\s\S]+?)\*\*/);
if (boldMatch) {
return {
type: 'strong',
raw: boldMatch[0],
text: boldMatch[1],
tokens: this.lexer.inlineTokens(boldMatch[1]),
};
}
// Italic: 同理
const emMatch = src.match(/^\*([\s\S]+?)\*/);
if (emMatch) {
return {
type: 'em',
raw: emMatch[0],
text: emMatch[1],
tokens: this.lexer.inlineTokens(emMatch[1]),
};
}
return false;
},
};
marked.use({ tokenizer });⚠ 已淘汰 — Legacy HTML 轉換層已於 2026-02-13 完全移除,資料庫所有文章皆為 Markdown 格式,不再需要 htmlToMarkdown() 函式,此情境僅作為歷史參考。
情境 2:Markdown 中嵌入 Font Awesome icon
文章中使用 <i class="fas fa-circle-check"></i> 嵌入 Font Awesome icon 時,如果有一個 HTML→Markdown 的轉換函式,它可能會把 <i> 標籤當成「斜體」轉換,導致空的 <i> 變成 **(兩個空斜體星號)。
// 原始 Markdown:
✓ 完成項目 <i class="fas fa-circle-check"></i>
// htmlToMarkdown() 處理後:
✓ 完成項目 **
// ↑ <i></i> 被 regex 轉成 *空字串*,變成兩個星號解法:在轉換前用 placeholder 保護 Font Awesome 的 <i> 標籤,轉換完成後再還原。
// 先保護 FA <i> 標籤
const faIcons: string[] = [];
markdown = markdown.replace(
/<i\s+[^>]*class="[^"]*fa-[^"]*"[^>]*><\/i>/gi,
(match) => {
faIcons.push(match);
return `%%FA_ICON_${faIcons.length - 1}%%`;
}
);
// 轉換普通 <i> 為斜體
markdown = markdown.replace(/<i[^>]*>(.*?)<\/i>/gi, '*$1*');
// 還原 FA icon
markdown = markdown.replace(/%%FA_ICON_(\d+)%%/g, (_, idx) => {
return faIcons[parseInt(idx, 10)];
});⚠ 已淘汰 — Legacy HTML 轉換層已於 2026-02-13 完全移除。資料庫 13 篇文章經查全部為 Markdown 格式(0 篇 HTML),格式偵測邏輯不再需要,此情境僅作為歷史參考。
情境 3:Markdown vs Legacy HTML 自動偵測
當系統同時存在舊版(Quill 編輯器產出的 HTML)和新版(Markdown)文章時,不能對所有內容都執行 HTML→Markdown 轉換——否則已經是 Markdown 的文章會被破壞。需要一個偵測函式判斷內容格式。
function detectContentFormat(content: string): 'markdown' | 'html' {
if (!content) return 'markdown';
if (!/<[^>]+>/.test(content)) return 'markdown';
// Markdown 特徵:## 標題、**粗體**、``` code fence、[link](url) 等
const markdownSignals = [
/^#{1,6}\s/m, // # heading
/\*\*[^*]+\*\*/, // **bold**
/```[\s\S]*?```/, // code fence
/\[.+?\]\(.+?\)/, // [text](url)
/^\s*[-*+]\s/m, // - list item
];
// HTML 結構特徵:<p>、<div>、<strong> 等
const htmlSignals = [
/<p[^>]*>/i, /<div[^>]*>/i, /<h[1-6][^>]*>/i,
/<strong[^>]*>/i, /<em[^>]*>/i,
];
// 有任何 Markdown 語法 → 判定為 Markdown(即使含 inline HTML)
for (const pattern of markdownSignals) {
if (pattern.test(content)) return 'markdown';
}
for (const pattern of htmlSignals) {
if (pattern.test(content)) return 'html';
}
return /^\s*</.test(content) ? 'html' : 'markdown';
}關鍵原則:Markdown 優先
偵測邏輯優先檢查 Markdown 特徵。因為「Markdown 含 inline HTML」(如 Font Awesome icon)是合法的 Markdown, 不應被當成 Legacy HTML 處理。只有在完全沒有 Markdown 語法、且有 HTML 結構標籤時,才判定為 HTML。
情境 4:DOMPurify 過濾掉 inline HTML
使用 dangerouslySetInnerHTML 渲染 Markdown 轉出的 HTML 時,必須用 DOMPurify 做安全過濾。但預設配置會移除 <i> 標籤的 class 屬性,導致 Font Awesome icon 失效。
import DOMPurify from 'isomorphic-dompurify';
const rawHtml = marked.parse(content, { renderer }) as string;
// 必須明確允許 <i> 標籤和 class 屬性
const safeHtml = DOMPurify.sanitize(rawHtml, {
ADD_TAGS: ['i'],
ADD_ATTR: ['class', 'aria-hidden', 'role', 'style'],
});CJK Markdown 渲染 Edge Case 總覽
| 情境 | 症狀 | 根因 | 解法 |
|---|---|---|---|
**粗體**中文 | 星號原封不動顯示 | CommonMark 右側 delimiter 規則 | 自訂 emStrong tokenizer |
FA <i> icon 已淘汰 | 顯示 ** | htmlToMarkdown 把 <i> 當斜體 | Legacy 層已移除 |
| Markdown 含 HTML 已淘汰 | 整篇內容被破壞 | htmlToMarkdown 誤判格式 | Legacy 層已移除 |
| DOMPurify 過濾 | icon 消失 | 預設移除 <i> 的 class | ADD_TAGS + ADD_ATTR |
結語
設計系統是一項長期投資。短期內它需要團隊投入時間與精力來建立和維護,但長期來看,它能大幅減少重複工作、提升產品品質、加速新功能的開發速度。對於任何認真對待使用者體驗的團隊來說,設計系統都是值得投入的基礎建設。
特別是對於繁體中文產品,排版的品質直接影響使用者對產品的信任感與專業度。從字型選擇、字級比例到行高行距,每一個看似微小的決定都在塑造整體的閱讀體驗。我們相信,花時間在這些基礎上是值得的。
我們會持續在 Brickverse Design System 中加入更多元件與模板,為開發者提供更完整的工具鏈。如果你對我們的設計決策有任何想法或建議,歡迎與我們交流。
Brickverse 設計團隊
我們是一群熱愛設計與開發的團隊,致力於打造最適合繁體中文環境的設計系統。 從 Token 到元件、從排版到動畫,我們用系統化的思維解決每一個設計問題。