Guide / 15 min

軟體工程師的設計原則

軟體工程師的設計原則:完整指南

2026年2月更新

2026年2月更新: 新增兩個章節——互動模式(研究 Framer、Flighty、Halide、Warp、Bear、Craft 和 Superhuman 後歸納的8種範式)以及 AI 介面模式(引用優先設計、串流階段、來自 Perplexity 的錯誤透明度)。將網頁模式更新至2026年,涵蓋錨點定位、捲動驅動動畫及 @starting-style。更新無障礙章節以反映 WCAG 2.2 的 ISO 標準化。請參閱設計研究深入了解16款卓越產品。

多年來,我一邊打造軟體,一邊鑽研設計——從 Dieter Rams 等傳奇大師身上汲取原則,拆解 Linear、Stripe 和 Raycast 等產品的介面。這份指南將這些理解濃縮為一份全面的參考資源,正是我當初開始在意軟體外觀與體驗時,最希望擁有的那種。

設計不是裝飾,而是溝通。每一個像素都在傳達功能、層級與意義。業餘感與專業感之間的差距,在於是否理解這些原則並一致地加以運用。

本指南假設您已具備程式撰寫能力。它要教會您的是「看見」——理解為何有些介面讓人感到毫不費力,而其他介面卻令人混亂,更重要的是,學會如何打造前者。


目錄

第一部分:基礎篇

  1. Gestalt 心理學
  2. 字型排版
  3. 色彩理論
  4. 視覺層級
  5. 間距與節奏
  6. 動畫原則

第二部分:互動與 AI

  1. 互動模式
  2. AI 介面模式

第三部分:設計哲學

  1. Dieter Rams:十大原則

第四部分:實作篇

  1. 2026 年 Web 模式
  2. Design Tokens 系統
  3. 正確實作 Dark Mode
  4. Figma 設計稿匯出流程

第五部分:參考資源

  1. 快速參考表
  2. 設計檢核清單
  3. 設計案例研究

Gestalt 心理學

「整體不同於各部分的總和。」— Kurt Koffka

Gestalt 心理學發源於 1920 年代的德國,旨在解釋人類如何感知視覺資訊。大腦並非逐一辨識像素,而是將元素組織成有意義的模式。掌握這些原則,便能主導使用者對介面的認知方式。

鄰近性(Proximity)

彼此靠近的元素會被視為一組。

這是 UI 設計中最強大的 Gestalt 原則。間距所傳達的關聯性,勝過任何其他視覺屬性。

WRONG (equal spacing = no grouping):
┌─────────────────┐
│ Label           │
│                 │
│ Input Field     │
│                 │
│ Label           │
│                 │
│ Input Field     │
└─────────────────┘

RIGHT (unequal spacing = clear groups):
┌─────────────────┐
│ Label           │
│ Input Field     │ ← Tight (4px) - related
│                 │
│                 │ ← Wide (24px) - separating groups
│ Label           │
│ Input Field     │ ← Tight (4px) - related
└─────────────────┘

CSS 實作:

.form-group {
  margin-bottom: 24px;  /* Between groups: wide */
}

.form-group label {
  margin-bottom: 4px;   /* Label to input: tight */
  display: block;
}

SwiftUI 實作:

VStack(alignment: .leading, spacing: 4) {  // Tight within group
    Text("Email")
        .font(.caption)
        .foregroundStyle(.secondary)
    TextField("[email protected]", text: $email)
        .textFieldStyle(.roundedBorder)
}
.padding(.bottom, 24)  // Wide between groups

相似性(Similarity)

具有相同視覺特徵的元素會被認為彼此相關。

當元素外觀一致時,使用者會假設它們的功能也相同。這正是設計系統採用一致按鈕樣式、卡片處理方式和字型排版的原因。

Example Navigation:
┌───────────────────────────────────┐
 [Dashboard] [Projects] [Settings]    Same style = same function
                                   
 ┌─────┐  ┌─────┐  ┌─────┐        
 Card   Card   Card             Same style = same content type
 └─────┘  └─────┘  └─────┘        
                                   
 [+ New Project]                      Different style = different function
└───────────────────────────────────┘

圖地關係(Figure-Ground)

內容應與背景明確區隔。

大腦需要區分「圖」(聚焦對象)與「地」(背景)。圖地關係處理不當會造成視覺混亂。

常用手法: - 對比(淺色圖形搭配深色背景,或反之) - 陰影(將圖形提升至背景之上) - 邊框(界定圖形邊緣) - 模糊(模糊背景、銳化前景)

/* Strong figure-ground relationship */
.card {
  background: var(--color-surface);     /* Figure */
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);  /* Elevation */
}

.modal-overlay {
  background: rgba(0, 0, 0, 0.5);  /* Dim ground */
  backdrop-filter: blur(4px);      /* Blur ground */
}

共同區域(Common Region)

位於同一邊界內的元素會被視為同一群組。

將元素包圍在視覺容器中(卡片、方框、邊框區域),即暗示它們彼此關聯。

連續性(Continuity)

視線會自然沿著路徑、線條與曲線移動。

善用對齊與視覺動線,引導使用者的注意力流動於介面之中。

CONTINUITY IN ALIGNMENT:
┌────────────────────────────────┐
 Logo    [Nav]  [Nav]  [Nav]      Aligned on horizontal axis
├────────────────────────────────┤
                                
 Headline                       
 ─────────────────────────────     Eye follows left edge
 Paragraph text continues       
 along the same left edge       
                                
 [Primary Action]                  Still on the left edge
└────────────────────────────────┘

封閉性(Closure)

大腦會自動補全不完整的形狀。

使用者不需要看到每一個像素——他們會在腦中自動補完熟悉的形狀。這使得更簡約、更優雅的設計成為可能。

/* Horizontal scroll with partial card (closure) */
.card-carousel {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  padding-right: 48px;  /* Show partial card = scroll hint */
}

.card-carousel .card {
  flex: 0 0 280px;  /* Fixed width, partial visible */
}

Gestalt 快速參考

原則 規則 主要用途
鄰近性 相關者靠近,無關者拉遠 表單欄位、內容區塊
相似性 外觀相同 = 功能相同 按鈕、卡片、導覽列
圖地關係 層次之間清楚分離 卡片、對話框、覆蓋層
共同區域 邊界將內容歸組 設定區塊、使用者卡片
連續性 沿線條與對齊方向移動 時間軸、閱讀動線
封閉性 大腦會補完形狀 圖示、滾動提示、骨架畫面

字型排版

「字型排版是一門技藝,賦予人類語言持久的視覺形式。」— Robert Bringhurst

字型排版是介面設計的根基。文字傳遞功能、層級與品牌特質。拙劣的排版讓介面更難使用;出色的排版則渾然天成——使用者甚至察覺不到它的存在。

字級系統(Type Scale)

一致的字級系統能營造視覺和諧感,建議採用數學比例來建構。

1.25 倍比例(推薦用於 UI):

:root {
  /* Base: 16px (1rem) */
  --text-xs: 0.64rem;    /* 10.24px - use sparingly */
  --text-sm: 0.8rem;     /* 12.8px - captions, labels */
  --text-base: 1rem;     /* 16px - body text */
  --text-lg: 1.25rem;    /* 20px - lead text */
  --text-xl: 1.563rem;   /* 25px - h4 */
  --text-2xl: 1.953rem;  /* 31.25px - h3 */
  --text-3xl: 2.441rem;  /* 39px - h2 */
  --text-4xl: 3.052rem;  /* 48.8px - h1 */
}

行高(Leading)

行高對可讀性影響甚鉅,不同類型的內容需要不同的行距設定。

內容類型 行高 原因
標題 1.1 - 1.2 緊湊、粗體、篇幅短
UI 文字 1.3 - 1.4 標籤、按鈕
內文 1.5 - 1.7 確保段落的可讀性
長篇閱讀 1.7 - 2.0 文章、技術文件

行寬(Measure)

最佳行寬能減少視覺疲勞,提升閱讀理解力。

  • 最佳範圍:每行 45-75 個字元
  • 目標值:50-65 個字元
  • 絕對上限:85 個字元
p {
  max-width: 65ch;  /* ch unit = width of '0' character */
}

.article-body {
  max-width: 70ch;
  margin: 0 auto;
}

字型選擇

優先使用系統字型。它們載入瞬間完成、與作業系統風格一致,且針對螢幕顯示已充分最佳化。

:root {
  --font-sans: system-ui, -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;

  --font-mono: ui-monospace, 'SF Mono', 'Cascadia Code',
               'JetBrains Mono', Consolas, monospace;
}

適合使用自訂字型的情境: - 品牌差異化(行銷網站) - 營造編輯或出版物的質感 - 系統字型無法滿足的特定設計意圖

以字重建立層級

善用字重來建立層級,而非僅依賴字級大小。

h1 { font-weight: 700; }  /* Bold */
h2 { font-weight: 600; }  /* Semibold */
h3 { font-weight: 600; }  /* Semibold */
.lead { font-weight: 500; }  /* Medium */
p { font-weight: 400; }   /* Regular */
.meta { font-weight: 400; color: var(--text-muted); }

字型排版快速參考

屬性 內文 標題 UI 標籤
字級 16-18px 24-48px 12-14px
字重 400 600-700 500
行高 1.5-1.7 1.1-1.2 1.3-1.4
行寬 45-75ch 不適用 不適用
對齊 靠左 可置中 靠左

色彩理論

「色彩是一種直接影響靈魂的力量。」——Wassily Kandinsky

色彩的傳達速度遠快於文字。它奠定氛圍、引導注意力、傳遞意義,並建立品牌識別。

60-30-10 法則

這是打造視覺均衡介面最可靠的色彩分配原則。

┌──────────────────────────────────────────┐
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│ 60% - 主色調(背景)
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 30% - 輔助色(卡片、區塊)
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 10% - 強調色(按鈕、連結)
└──────────────────────────────────────────┘

建構色彩調色盤

每個介面都需要以下語意色彩:

:root {
  /* Brand */
  --color-primary: hsl(220, 80%, 50%);
  --color-primary-hover: hsl(220, 80%, 45%);

  /* Semantic */
  --color-success: hsl(142, 76%, 36%);  /* Green - positive */
  --color-warning: hsl(38, 92%, 50%);   /* Amber - caution */
  --color-error: hsl(0, 84%, 60%);      /* Red - danger */

  /* Neutrals */
  --color-background: hsl(0, 0%, 100%);
  --color-surface: hsl(220, 14%, 96%);
  --color-border: hsl(220, 13%, 91%);

  /* Text */
  --color-text: hsl(220, 13%, 13%);
  --color-text-secondary: hsl(220, 9%, 46%);
  --color-text-muted: hsl(220, 9%, 64%);
}

色彩心理學

色彩 心理意涵 UI 應用場景
藍色 信任、穩定、沉著 金融、科技、企業
綠色 成長、自然、成功 健康、環保、正向狀態
紅色 活力、緊迫、危險 警示、促銷、錯誤
橘色 溫暖、熱忱 CTA、活潑品牌
黃色 樂觀、警示 警告、重點標示
紫色 奢華、創意 高端產品

深色模式優先設計(Vercel)

Vercel 採用深色模式優先設計,再據此推導出淺色模式。這種做法能產出更優質的深色介面,因為深色模式成為首要考量,而非事後補救。

/* Design dark first, derive light */
:root {
  /* Dark mode defaults */
  --color-background: hsl(0, 0%, 0%);
  --color-surface: hsl(0, 0%, 7%);
  --color-border: hsl(0, 0%, 15%);
  --color-text: hsl(0, 0%, 93%);
  --color-text-secondary: hsl(0, 0%, 63%);
}

@media (prefers-color-scheme: light) {
  :root {
    --color-background: hsl(0, 0%, 100%);
    --color-surface: hsl(0, 0%, 97%);
    --color-border: hsl(0, 0%, 89%);
    --color-text: hsl(0, 0%, 9%);
    --color-text-secondary: hsl(0, 0%, 40%);
  }
}

適用情境: 開發者工具、媒體應用、儀表板——使用者長時間操作的場景,深色模式能有效減輕眼睛疲勞。

無障礙對比度

等級 一般文字 大型文字 UI 元件
AA 4.5:1 3:1 3:1
AAA 7:1 4.5:1 不適用

WCAG 2.2 已於2025年10月成為 ISO 標準(ISO/IEC 40500:2025),新增焦點可見性、冗餘輸入及無障礙驗證等準則。主要新增項目:焦點指示器不得被其他內容完全遮蔽(2.4.11),且驗證機制不得僅依賴認知功能測試(3.3.8)。

工具: WebAIM Contrast Checker、Chrome DevTools 色彩選取器


視覺層級

「設計是品牌的無聲大使。」——Paul Rand

視覺層級決定使用者先看到什麼、再看到什麼、最後注意什麼。缺乏明確層級,使用者得費力尋找資訊;層級分明,介面則渾然天成。

層級的六大工具

1. 尺寸 ——元素越大,越先吸引目光

.hero-title { font-size: 3rem; }      /* Dominant */
.section-title { font-size: 1.5rem; } /* Secondary */
.body-text { font-size: 1rem; }       /* Baseline */

2. 字重 ——粗體凸顯,細體退居幕後

h1 { font-weight: 700; }
.lead { font-weight: 500; }
p { font-weight: 400; }

3. 色彩與對比 ——高對比度等於高關注度

.title { color: var(--color-text); }  /* Near black */
.meta { color: var(--color-text-muted); }  /* Gray */

4. 位置 ——關鍵位置舉足輕重

F-PATTERN (content pages):     Z-PATTERN (landing pages):
████████████████████████      1 ──────────────────► 2
████████                            ↘
████                                     ↘
██                                            ↘
                                   3 ──────────────────► 4

5. 留白 ——孤立凸顯重要性

.hero { padding: 120px 48px; }  /* Generous space */
.data-table { padding: 12px; }  /* Dense content */

6. 深度與立體感 ——浮起的元素自然奪取注意力

:root {
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
  --shadow-md: 0 4px 6px rgba(0,0,0,0.1);
  --shadow-lg: 0 10px 15px rgba(0,0,0,0.1);
}

.card { box-shadow: var(--shadow-sm); }
.card:hover { box-shadow: var(--shadow-md); }
.modal { box-shadow: var(--shadow-lg); }

實際應用模式

協作存在感(Figma): 多位使用者的游標搭配名稱標籤、選取高亮與元件外框,共同構成一份活文件。每位協作者的顏色各不相同,但視覺權重相當——沒有任何游標比其他的更「搶眼」。

環境狀態指示(Vercel): 部署狀態採用細膩而持續的指示器,而非干擾式的彈出警告。頂端的一條細色帶即傳達當前狀態(建置中、已部署、失敗),不打斷工作流程。

真實世界的設計隱喻(Flighty): 航班進度視覺化仿照實體航空儀表——高度曲線、速度指示器與登機門地圖採用使用者熟悉的視覺隱喻,取代抽象的進度條。

瞇眼測試

瞇起眼睛看你的設計。如果依然能辨識出層級架構,就代表層級夠強。


間距與節奏

「留白如同空氣:設計需要呼吸。」——Wojciech Zieliński

間距是設計中隱而不見的結構。一致的間距營造視覺節奏——讓元素彷彿天生屬於同一套系統。

8px 網格

8px 網格是業界標準,原因如下: - 可被均勻劃分(8、16、24、32、40、48⋯⋯) - 適用於常見螢幕密度(1x、1.5x、2x、3x) - 無需額外計算即可建立一致節奏

:root {
  --space-1: 4px;    /* Tight: icon gaps */
  --space-2: 8px;    /* Compact: inline elements */
  --space-3: 12px;   /* Snug: form fields */
  --space-4: 16px;   /* Default: most gaps */
  --space-6: 24px;   /* Spacious: card padding */
  --space-8: 32px;   /* Section gaps */
  --space-12: 48px;  /* Major sections */
  --space-16: 64px;  /* Page sections */
  --space-20: 80px;  /* Hero spacing */
}

內部間距與外部間距

內部間距(padding): 元素內部的空間 外部間距(margin): 元素之間的空間

原則: 相關群組內,內部間距通常應大於外部間距。

.card {
  padding: 24px;        /* Internal: spacious */
  margin-bottom: 16px;  /* External: less than padding */
}

元件間距模式

卡片:

.card { padding: 24px; border-radius: 12px; }
.card-header { margin-bottom: 16px; }
.card-title { margin-bottom: 4px; }  /* Tight to subtitle */

按鈕:

.btn { padding: 12px 24px; border-radius: 8px; }
.btn--sm { padding: 8px 16px; }
.btn--lg { padding: 16px 32px; }
.btn-group { display: flex; gap: 12px; }

表單:

.form-row { margin-bottom: 24px; }
.form-label { margin-bottom: 4px; }
.form-help { margin-top: 4px; }
.form-actions { margin-top: 32px; display: flex; gap: 12px; }

間距速查表

使用情境 建議間距
圖示與文字 4-8px
標籤與輸入欄 4px
表單群組之間 24px
卡片內距 20-24px
卡片間距 16-24px
區塊內距(行動裝置) 48-64px
區塊內距(桌面) 80-96px
按鈕內距(水平/垂直) 24px / 12px

動畫原則

「動畫不是會動的繪畫藝術,而是被繪製出來的動態藝術。」—— Norman McLaren

動畫為介面注入生命力。運用得當,能引導注意力、傳達狀態、建立情感連結;運用不當,則令人煩躁分心。

核心原則

動畫應該讓人覺得理所當然,而非裝飾點綴。

好的動畫: 1. 傳達靜態設計無法表達的訊息 2. 透過展示關聯性來降低認知負擔 3. 感覺自然且符合預期 4. 不會讓使用者意識到它的存在

差的動畫: 1. 純粹因為「看起來很酷」而存在 2. 拖慢使用者的操作速度 3. 刻意吸引注意力 4. 製造焦慮或不耐煩

UI 關鍵原則

1. 預備動作(Anticipation) —— 為即將發生的事做好心理準備。

.button {
  transition: transform 0.1s ease-out;
}

.button:active {
  transform: scale(0.97);  /* Slight press before action */
}

2. 跟隨動作(Follow-Through) —— 讓動態自然完成,如彈簧般漸趨穩定。

.panel {
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
withAnimation(.spring(response: 0.4, dampingFraction: 0.7)) {
    isOpen = true
}

3. 緩入緩出(Ease-In, Ease-Out) —— 自然界中沒有東西以恆速運動。

曲線 使用時機 特性
ease-out 元素進場 快速啟動,緩慢停止
ease-in 元素離場 緩慢啟動,快速離開
ease-in-out 狀態切換 全程平滑
linear 載入指示器 持續、機械式

4. 場景調度(Staging) —— 將注意力引導至重要之處。除非經過編排的群組動畫,否則同一時間只應有一個元素在移動。

5. 交錯排列(Staggering) —— 元素應依序出現,而非同時湧入。

.list-item {
  animation: fadeSlideIn 0.3s ease-out both;
}

.list-item:nth-child(1) { animation-delay: 0ms; }
.list-item:nth-child(2) { animation-delay: 50ms; }
.list-item:nth-child(3) { animation-delay: 100ms; }
.list-item:nth-child(4) { animation-delay: 150ms; }

@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

時間長度指南

時長 使用場景 感受
50-100ms 微互動(懸停、按壓) 即時回饋
150-200ms 簡單狀態變化(切換、選取) 俐落
250-350ms 中等過渡(面板滑動、卡片翻轉) 順暢
400-500ms 大幅移動(頁面過渡、彈出視窗) 從容

效能:黃金法則

只對 transformopacity 做動畫 —— 這兩者經過GPU加速,不會觸發重新佈局。

/* BAD: Animating layout */
.panel { transition: left 0.3s, width 0.3s; }

/* GOOD: Using transform */
.panel { transition: transform 0.3s; }

何時不該使用動畫

  1. 使用者啟用了 prefers-reduced-motion css @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }

  2. 動畫未提供額外資訊 —— 多餘的旋轉圖示、彈跳元素

  3. 使用者趕時間 —— 錯誤狀態、表單驗證、搜尋結果
  4. 動畫會拖慢重複操作 —— 鍵盤快捷鍵應跳過動畫
  5. 資料已經載入 —— Bear 透過預先載入內容來消除所有載入狀態,讓應用程式感覺即時回應。如果能預先載入,就直接跳過骨架屏和旋轉圖示。
// Bear's approach: preload so no loading state is needed
struct NoteListView: View {
    @Query var notes: [Note]  // SwiftData loads from disk instantly
    // No loading state, no skeleton, no spinner — data is always there
    var body: some View {
        List(notes) { note in
            NoteRow(note: note)
        }
    }
}

動畫快速參考

:root {
  /* Durations */
  --duration-instant: 0.1s;
  --duration-fast: 0.15s;
  --duration-normal: 0.25s;
  --duration-slow: 0.4s;

  /* Easings */
  --ease-out: cubic-bezier(0.0, 0.0, 0.58, 1.0);
  --ease-in: cubic-bezier(0.42, 0.0, 1.0, 1.0);
  --ease-in-out: cubic-bezier(0.42, 0.0, 0.58, 1.0);
  --ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
}

互動模式

「最好的介面就是沒有介面。」—— Golden Krishna

互動模式定義使用者如何操控、導覽和理解產品。這些模式是從研究那些提供卓越互動體驗的產品中萃取而來。

直接操控(Framer)

將抽象概念化為具體操作。Framer 把CSS斷點——原本只是抽象數字——轉化為可拖曳的控制桿,讓使用者即時看到版面配置的變化。

/* Breakpoint handle styling */
.breakpoint-handle {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 4px;
  background: var(--accent);
  cursor: col-resize;
  opacity: 0.6;
  transition: opacity 0.15s ease;
}

.breakpoint-handle:hover,
.breakpoint-handle:active {
  opacity: 1;
  width: 6px;
}

適用時機:任何結果可視化的設定——拖曳調整大小、色彩選擇器、時間軸拖移。

情境感知介面(Flighty、Figma)

只顯示與當下情境相關的內容。Flighty 為航班追蹤設計了 15 種不同狀態。Figma 的屬性面板會根據選取的物件完全變換。

階段(Flighty) 使用者看到的內容
起飛前 24 小時 確認碼、航廈資訊
抵達機場 登機門編號、登機時間
飛行中 剩餘時間、航程進度、預計抵達時間
降落 轉機登機門、步行路線
enum ContextState: CaseIterable {
    case farOut, dayBefore, headToAirport, atAirport
    case atGate, boarding, inFlight, landed, connection

    static func current(for flight: Flight, context: UserContext) -> ContextState {
        // Factor in: time, location, flight status
        // Return the single most relevant state
    }
}

反面模式:顯示所有控制項,再把不相關的灰掉。這只會製造視覺雜訊。

智慧啟動(Halide)

工具應能感知情境並自動啟動。Halide 的對焦放大鏡在拖曳對焦時自動出現,放開時自動消失,完全不需要切換按鈕。

struct IntelligentlyActivated<Content: View>: ViewModifier {
    let isInteracting: Bool
    @State private var isVisible = false

    func body(content: Content) -> some View {
        content
            .opacity(isVisible ? 1 : 0)
            .scaleEffect(isVisible ? 1 : 0.95)
            .animation(.easeInOut(duration: 0.2), value: isVisible)
            .onChange(of: isInteracting) { _, newValue in
                if newValue {
                    withAnimation { isVisible = true }
                } else {
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
                        if !isInteracting { isVisible = false }
                    }
                }
            }
    }
}

雙模式設計(Halide、Warp)

模式切換應該徹底改變介面,而非僅僅切換元素的顯示與隱藏。Halide 的自動模式和手動模式是截然不同的兩套介面。Warp 透過四種輸入方式(打字、指令面板、AI、滑鼠)巧妙地橋接了CLI與圖形介面,不強迫使用者侷限於單一操作範式。

結構化內容(Warp、Bear、Craft)

為傳統上缺乏結構的內容賦予組織性。Warp 將終端機輸出轉化為獨立區塊,可複製、分享或重新執行。Bear 讓使用者在書寫過程中以行內方式組織筆記(#tag/subtag)。Craft 則讓任何區塊都能成為一個頁面——結構從使用中自然浮現,而非來自預先定義的層級架構。

漸進式訓練(Superhuman)

透過反覆接觸教會使用者最快的操作方式。Superhuman 的 Cmd+K 指令面板總是在搜尋結果旁顯示鍵盤快捷鍵,每一次使用都是一堂微型課程。

/* Always show shortcut alongside command name */
.command-result {
  display: flex;
  justify-content: space-between;
  padding: 8px 12px;
}

.command-shortcut {
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
  background: var(--bg-subtle);
  padding: 2px 6px;
  border-radius: 4px;
}

反面模式:用教學彈出視窗來解釋功能。解釋容易遺忘,實際操作才會記住。


AI 介面模式

「最好的 AI 介面,讓機器的運作過程清晰可見,讓機器的輸出結果可供驗證。」

AI 介面面臨獨特挑戰:使用者無法預測輸出結果、無法透過直覺判斷正確性,也往往分不清系統是在正常運作還是已經出錯。

核心問題

傳統軟體 AI 軟體
輸出可預測 輸出因情境而異
錯誤顯而易見 錯誤看起來似是而非
使用者透過測試來驗證 使用者透過查證來源來驗證
載入中=等待 載入中=正在處理(應呈現過程)
預設信任 信任需要累積

引用優先設計(Perplexity)

每個事實性陳述都必須連結到來源。Perplexity 在每個論述中嵌入行內引用 [1],並提供懸停預覽與常駐的來源面板。

.citation-marker {
  position: relative;
  color: var(--accent);
  cursor: pointer;
  font-size: 0.8em;
  vertical-align: super;
}

.citation-preview {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  width: 280px;
  padding: 12px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}

反面模式: AI 介面產生無法追溯來源的論述。如果模型無法引用出處,介面就應該標示警告。

串流階段指示器(Perplexity)

讓使用者看到 AI 正在做什麼,而非僅僅顯示「運作中」。以階段指示器取代千篇一律的旋轉圖示:「搜尋中…」→「正在閱讀 4 個來源…」→「撰寫回覆中…」

.phase-indicator {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 12px;
  background: color-mix(in srgb, var(--phase-color) 10%, transparent);
  border-radius: 16px;
  font-size: 13px;
  color: var(--phase-color);
  transition: all 0.3s ease;
}

.loading-dots span {
  width: 4px;
  height: 4px;
  background: currentColor;
  border-radius: 50%;
  animation: pulse 1.4s ease-in-out infinite;
}

錯誤透明化

AI 失敗或不確定時,應清楚呈現——切勿用看似自信的文字來掩飾。

情境 不良模式 良好模式
低信心度 以肯定語氣陳述 「我不太確定,但…」搭配較低調的樣式
找不到來源 捏造文字 「我找不到此論述的相關來源」
來源相互矛盾 默默選一個 同時呈現並標示衝突之處
資訊過時 當作最新資訊呈現 「截至 [日期]…」搭配時效指示器

關鍵洞察: 使用者能原諒坦承不確定的 AI,卻無法原諒信誓旦旦卻答錯的 AI。


Dieter Rams:十項原則

「少,但更好。」— Dieter Rams

Dieter Rams 是 20 世紀最具影響力的工業設計師。1961 年至 1995 年間,他擔任 Braun 設計主管,打造出歷經數十年依然不朽的產品。他的作品直接啟發了 Apple 的設計語言。

好設計的十項原則

1. 好設計是創新的 不要模仿。讓進步的技術與創新的設計相輔相成。

2. 好設計使產品實用 每個元素都必須有其用途。形式追隨功能。

3. 好設計是美的 美不是膚淺的裝飾——而是不可或缺的本質。每天使用的產品影響著我們的身心。

4. 好設計使產品易於理解 使用者不應該需要說明書。介面本身就是最好的導師。

5. 好設計是不張揚的 設計應輔助而非喧賓奪主。使用者的內容才是主角,不是你的 UI。

/* Obtrusive: UI competes with content */
.editor {
  background: linear-gradient(135deg, purple, blue);
  border: 3px dashed gold;
}

/* Unobtrusive: UI recedes, content shines */
.editor {
  background: var(--color-background);
  border: 1px solid var(--color-border);
}

6. 好設計是誠實的 不使用暗黑模式。不過度承諾。對限制保持透明。

7. 好設計是持久的 避免稍縱即逝的潮流。經典勝於時髦。

TRENDY (will date):           TIMELESS:
- Extreme glassmorphism       - Clean typography
- Neon colors, glitch effects - Subtle elevation
- Aggressive gradients        - Neutral palette with considered accent

8. 好設計連最後的細節都一絲不苟 沒有任何東西可以是隨意的。載入狀態、空狀態、錯誤狀態——全都需要精心設計。

9. 好設計是環保的 效能就是環保。尊重使用者的注意力。寫出高效的程式碼。

10. 好設計是盡可能少的設計 移除一切不必要之物。最好的設計是無形的。


2026 年 Web 模式

現代網頁設計善用原生 CSS 能力,在許多情境下無需依賴 JavaScript。2025 至 2026 年間,錨點定位、捲動驅動動畫與 @starting-style 已正式進入主流瀏覽器。

Container Queries

根據元件的容器尺寸而非視窗大小來調整元件樣式。

.card-grid {
  container-type: inline-size;
  container-name: card-grid;
}

.card {
  display: grid;
  gap: 16px;
  padding: 20px;
}

@container card-grid (min-width: 400px) {
  .card {
    grid-template-columns: auto 1fr;
  }
}

@container card-grid (min-width: 600px) {
  .card {
    padding: 32px;
    gap: 24px;
  }
}

:has() 選取器

根據子元素來選取父元素——過去沒有 JavaScript 根本做不到。

/* Card with image gets different padding */
.card:has(img) {
  padding: 0;
}

.card:has(img) .card-content {
  padding: 20px;
}

/* Form group with error */
.form-group:has(.input:invalid) .form-label {
  color: var(--color-error);
}

/* Highlight navigation when on that page */
.nav-item:has(a[aria-current="page"]) {
  background: var(--color-surface);
}

CSS 原生巢狀

無需預處理器即可使用原生巢狀語法。

.card {
  background: var(--color-surface);
  border-radius: 12px;
  padding: 24px;

  & .card-title {
    font-size: 1.25rem;
    font-weight: 600;
    margin-bottom: 8px;
  }

  & .card-body {
    color: var(--color-text-secondary);
    line-height: 1.6;
  }

  &:hover {
    box-shadow: var(--shadow-md);
  }

  @media (min-width: 768px) {
    padding: 32px;
  }
}

HTMX 整合

伺服器驅動的互動體驗,無需笨重的 JavaScript 框架。

<!-- Load content on click -->
<button hx-get="/api/more-items"
        hx-target="#item-list"
        hx-swap="beforeend"
        hx-indicator="#loading">
  Load More
</button>

<!-- Form with inline validation -->
<form hx-post="/api/contact"
      hx-target="#form-response"
      hx-swap="outerHTML">
  <input type="email" name="email"
         hx-post="/api/validate-email"
         hx-trigger="blur"
         hx-target="next .error" />
  <span class="error"></span>
</form>

錨點定位

原生 CSS 的元素相對定位——不需要 JavaScript。工具提示、彈出框與下拉選單都能自動跟隨觸發元素。

/* Anchor an element to another */
.trigger {
  anchor-name: --my-trigger;
}

.tooltip {
  position: fixed;
  position-anchor: --my-trigger;
  top: anchor(bottom);
  left: anchor(center);
  translate: -50% 8px;
}

捲動驅動動畫

將動畫進度與捲動位置綁定。閱讀進度條、視差效果、依序顯現動畫——全部不需要 JavaScript。

/* Reading progress bar */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  background: var(--color-primary);
  transform-origin: left;
  animation: progress linear;
  animation-timeline: scroll();
}

@keyframes progress {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

@starting-style

為進入 DOM 的元素定義初始樣式——實現純 CSS 的進場動畫,無需 JavaScript。

.card {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 0.3s ease, transform 0.3s ease;

  @starting-style {
    opacity: 0;
    transform: translateY(10px);
  }
}

Design Tokens 系統

一套完整的 token 系統,確保應用程式的一致性。

:root {
  /* Colors */
  --color-text: #1a1a1a;
  --color-text-secondary: #666666;
  --color-text-muted: #999999;

  --color-background: #ffffff;
  --color-surface: #f8f9fa;
  --color-surface-elevated: #ffffff;

  --color-border: #e5e7eb;
  --color-primary: #3b82f6;
  --color-primary-hover: #2563eb;

  --color-success: #10b981;
  --color-warning: #f59e0b;
  --color-error: #ef4444;

  /* Typography */
  --font-sans: system-ui, -apple-system, sans-serif;
  --font-mono: "SF Mono", Consolas, monospace;

  --text-xs: 0.75rem;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.25rem;
  --text-2xl: 1.5rem;
  --text-3xl: 2rem;

  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.75;

  /* Spacing (8px base) */
  --space-1: 0.25rem;   /* 4px */
  --space-2: 0.5rem;    /* 8px */
  --space-3: 0.75rem;   /* 12px */
  --space-4: 1rem;      /* 16px */
  --space-6: 1.5rem;    /* 24px */
  --space-8: 2rem;      /* 32px */
  --space-12: 3rem;     /* 48px */
  --space-16: 4rem;     /* 64px */

  /* Borders */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;

  /* Shadows */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);

  /* Transitions */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --duration-fast: 100ms;
  --duration-normal: 200ms;
}

深色模式的正確做法

別只是反轉色彩——應針對深色情境重新設計。

@media (prefers-color-scheme: dark) {
  :root {
    /* Neutrals */
    --color-background: hsl(220, 13%, 10%);
    --color-surface: hsl(220, 13%, 15%);
    --color-surface-elevated: hsl(220, 13%, 18%);
    --color-border: hsl(220, 13%, 23%);

    /* Text (inverted) */
    --color-text: hsl(220, 9%, 93%);
    --color-text-secondary: hsl(220, 9%, 70%);
    --color-text-muted: hsl(220, 9%, 55%);

    /* Adjust saturation for dark mode */
    --color-primary: hsl(220, 80%, 60%);
    --color-success: hsl(142, 70%, 45%);
    --color-error: hsl(0, 80%, 65%);

    /* Shadows in dark mode need adjustment */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

深色模式原則: - 大面積色塊應降低飽和度 - 提高強調色的明度 - 加強陰影(深色背景下需要更高對比度) - 深色模式應有意識地設計,而非事後補救


Figma 提取工作流程

將設計稿轉化為生產程式碼,需要系統性地提取設計 tokens——包括色彩、字型、間距與效果,這些定義了整個設計語言。

Figma 變數匯出

Figma 原生的 Variables 功能提供最簡潔的提取路徑:

匯出步驟: 1. 開啟 Figma 檔案 → Local Variables 面板 2. 點選集合選單 → “Export to JSON” 3. 儲存為 figma-variables.json

JSON Token 結構:

{
  "colors": {
    "primitive": {
      "blue-500": { "value": "#3b82f6", "type": "color" },
      "blue-600": { "value": "#2563eb", "type": "color" }
    },
    "semantic": {
      "primary": { "value": "{colors.primitive.blue-500}", "type": "color" },
      "primary-hover": { "value": "{colors.primitive.blue-600}", "type": "color" }
    }
  },
  "spacing": {
    "1": { "value": "4px", "type": "spacing" },
    "2": { "value": "8px", "type": "spacing" },
    "4": { "value": "16px", "type": "spacing" }
  }
}

Token 轉換為 CSS

CSS 自訂屬性:

:root {
  /* Primitive colors (direct values) */
  --color-blue-50: #eff6ff;
  --color-blue-100: #dbeafe;
  --color-blue-500: #3b82f6;
  --color-blue-600: #2563eb;
  --color-blue-900: #1e3a8a;

  /* Semantic colors (reference primitives) */
  --color-primary: var(--color-blue-500);
  --color-primary-hover: var(--color-blue-600);
  --color-background: var(--color-white);
  --color-surface: var(--color-gray-50);

  /* Spacing (8px grid) */
  --space-1: 0.25rem;  /* 4px */
  --space-2: 0.5rem;   /* 8px */
  --space-4: 1rem;     /* 16px */
  --space-6: 1.5rem;   /* 24px */
  --space-8: 2rem;     /* 32px */

  /* Typography */
  --font-size-sm: 0.875rem;
  --font-size-base: 1rem;
  --font-size-lg: 1.125rem;
  --line-height-tight: 1.25;
  --line-height-normal: 1.5;

  /* Effects */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);
  --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07);
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
}

深色模式 Tokens:

@media (prefers-color-scheme: dark) {
  :root {
    --color-background: var(--color-gray-900);
    --color-surface: var(--color-gray-800);
    --color-text: var(--color-gray-100);
    --color-text-secondary: var(--color-gray-400);

    /* Adjusted shadows for dark mode */
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3);
    --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
  }
}

Token 轉換為 SwiftUI

Color 擴充:

import SwiftUI

extension Color {
    // MARK: - Primitive Colors
    static let blue50 = Color(hex: "eff6ff")
    static let blue500 = Color(hex: "3b82f6")
    static let blue600 = Color(hex: "2563eb")

    // MARK: - Semantic Colors
    static let brandPrimary = Color.blue500
    static let brandPrimaryHover = Color.blue600

    // MARK: - Surface Colors
    static let surfaceBackground = Color(light: .white, dark: Color(hex: "0f172a"))
    static let surfaceElevated = Color(light: Color(hex: "f8fafc"), dark: Color(hex: "1e293b"))
}

extension Color {
    init(hex: String) {
        // Standard hex parsing implementation
    }

    init(light: Color, dark: Color) {
        self.init(UIColor { traits in
            traits.userInterfaceStyle == .dark ? UIColor(dark) : UIColor(light)
        })
    }
}

間距常數:

enum Spacing {
    static let xs: CGFloat = 4    // --space-1
    static let sm: CGFloat = 8    // --space-2
    static let md: CGFloat = 16   // --space-4
    static let lg: CGFloat = 24   // --space-6
    static let xl: CGFloat = 32   // --space-8
}

// Usage
VStack(spacing: Spacing.md) {
    // ...
}
.padding(Spacing.lg)

設計師交付檢查清單

設計師應匯出的資產:

資產類型 格式 備註
色彩 Variables JSON 包含淺色與深色模式
字型 Styles 匯出 字體、大小、粗細、行高
間距 Variables JSON 需記錄基礎單位
圖示 SVG 線框式、單色
圖片 PNG @2x/@3x 或 WebP 含壓縮設定
元件 Figma 連結 供實作時參考

品質關卡標準:

  • [ ] 所有色彩皆定義為變數(無硬編碼 hex 值)
  • [ ] 字型使用已定義的文字樣式
  • [ ] 間距遵循網格系統(8px 為基礎)
  • [ ] 已提供深色模式變體
  • [ ] 互動狀態皆有文件記錄(hover、active、disabled)
  • [ ] 響應式斷點已標註
  • [ ] 無障礙需求已註明(對比度比例)

開發者收到的交付物:

  1. Token 檔案(JSON/CSS/Swift,依平台而定)
  2. 元件規格與尺寸標注
  3. 所需格式的資產匯出檔
  4. 互動文件(狀態、動畫)
  5. 無障礙標註

快速參考表

Gestalt 原則

原則 規則 應用場景
鄰近性 相關的元素靠近放置 表單、區段
相似性 外觀相同代表功能相同 按鈕、卡片
圖地關係 清楚區分前景與背景 對話框、卡片
連續性 順著視覺動線引導 時間軸、對齊
封閉性 大腦會自動補全形狀 圖示、捲動提示

字型排版

元素 大小 粗細 行高
內文 16px 400 1.5-1.7
標題 24-48px 600-700 1.1-1.2
UI 標籤 12-14px 500 1.3-1.4
說明文字 12px 400 1.4

色彩角色

角色 淺色模式 深色模式
背景 #ffffff #0f172a
表面 #f4f5f7 #1e293b
邊框 #e4e6ea #334155
文字 #1a1a2e #f1f5f9
次要文字 #6b7280 #94a3b8
主色 #3b82f6 #60a5fa
成功 #22c55e #4ade80
錯誤 #ef4444 #f87171

間距比例

Token 數值 用途
–space-1 4px 圖示間隙
–space-2 8px 行內元素
–space-4 16px 預設間距
–space-6 24px 卡片內距
–space-8 32px 區段間距
–space-16 64px 頁面區段

設計檢查清單

在任何介面上線前,請逐一確認:

Gestalt

  • [ ] 相關元素比無關元素距離更近(鄰近性)
  • [ ] 相同功能具有相同樣式(相似性)
  • [ ] 前景與背景層次分明(圖地關係)
  • [ ] 視線能自然流動於版面之間(連續性)

字型排版

  • [ ] 基礎字體大小至少 16px
  • [ ] 內文行高 1.5 以上
  • [ ] 每行字數不超過 75 個字元
  • [ ] 層級清晰(至少可辨識 3 個層次)
  • [ ] 全站使用一致的字級比例

色彩

  • [ ] 所有文字通過 4.5:1 對比度(WCAG AA)
  • [ ] 色彩並非唯一的資訊指標(搭配圖示或標籤)
  • [ ] 深色模式經過刻意設計
  • [ ] 遵循 60-30-10 色彩分配原則

視覺層級

  • [ ] 能一眼辨識出最重要的元素
  • [ ] 視線按預期順序流動
  • [ ] 每個區段僅有一個明確的 CTA
  • [ ] 字級比例前後一致

間距

  • [ ] 所有間距使用已定義的比例(無魔術數字)
  • [ ] 卡片與元件具有一致的內距
  • [ ] 行動裝置的間距舒適合宜
  • [ ] 網格對齊一致(以 8px 為基礎)

互動

  • [ ] 使用者能否不假思索地完成目標?
  • [ ] UI 是否能依據當前情境自適應?
  • [ ] 工具是否僅在相關時才顯示?
  • [ ] 重複使用是否能引導更快的操作方式?

AI 介面

  • [ ] 每一項事實陳述皆可溯源
  • [ ] 串流輸出呈現處理階段,而非僅顯示載入動畫
  • [ ] 錯誤狀態透明呈現,不加以隱藏
  • [ ] 低信心度的輸出在視覺上有所區別

Dieter Rams 檢驗

  • [ ] 是否還能移除任何元素?
  • [ ] 每個元素是否都有其功能?
  • [ ] 5 年後會不會顯得過時?
  • [ ] 是否已設計每一種狀態?

資源

書籍: - As Little Design as Possible,Sophie Lovell 著(Dieter Rams) - The Elements of Typographic Style,Robert Bringhurst 著

工具: - WebAIM Contrast Checker - Type Scale Generator - Figma Tokens Studio — 設計 token 管理工具

設計系統: - Apple HIG - Material Design 3 - Radix UI - shadcn/ui


設計研究

深入剖析 16 款卓越產品,記錄值得借鑑的設計模式與原則。

開發者工具

產品 核心貢獻
Figma 多人即時協作、情境感知面板
Warp 區塊式終端機、CLI-GUI 橋接
Framer 視覺化響應式設計、屬性控制項
Vercel 深色模式典範、環境狀態指示
Linear Optimistic UI、鍵盤優先工作流程
Raycast 擴充系統、快速操作

iOS 原生應用(Apple Design Award 得獎作品)

產品 核心貢獻
Flighty 15 種智慧狀態、Live Activities、資料視覺化
Halide 智慧啟動機制、手勢控制
Bear 排版優先、行內標籤系統
Craft 原生優先的跨平台設計、巢狀頁面
Things 延後日期、快速輸入模式

生產力與 AI

產品 核心貢獻
Superhuman 100ms 法則、命令面板引導、實作式新手教學
Perplexity 引用優先的 AI 介面、串流階段呈現
Notion 區塊系統、斜線命令
Arc 空間分區、分割檢視、命令列
Stripe 卓越的技術文件、API 設計

本指南在實踐中持續成長。設計原則歷久彌新,但其應用方式隨技術與理解不斷演進。