Guide / 15 min

软件工程师的设计原则

软件工程师的设计原则:完整指南

更新于2026年2月9日

2026年2月更新: 新增两个章节——交互模式(通过研究 Framer、Flighty、Halide、Warp、Bear、Craft 和 Superhuman 总结的8种范式)和 AI 界面模式(来自 Perplexity 的引用优先设计、流式输出阶段、错误透明度)。将 Web 模式更新至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. 暗色模式的正确实现
  4. Figma 导出工作流

第五部分:参考

  1. 速查表
  2. 设计清单
  3. 设计案例研究

Gestalt 心理学

“整体并非部分之和,而是另一回事。” — Kurt Koffka

Gestalt 心理学诞生于 1920 年代的德国,研究人类如何感知视觉信息。大脑并不逐个像素地处理画面,而是将元素组织为有意义的模式。掌握这些原则,就能主动引导用户对界面的感知。

邻近性

相互靠近的元素会被视为一组。

这是 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

相似性

具有相同视觉特征的元素会被视为相关联。

当元素外观一致时,用户会认为它们功能一致。这正是设计系统要求按钮样式、卡片样式和字体排印保持统一的原因。

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

图形-背景关系

内容应清晰地从背景中区分出来。

大脑需要区分”图形”(关注的焦点)和”背景”(衬托的底层)。图形-背景关系处理不当会造成视觉混乱。

常用技巧: - 对比度(亮色图形搭配暗色背景,或反之) - 阴影(将图形从背景中”抬起”) - 边框(勾勒图形轮廓) - 模糊(虚化背景,锐化前景)

/* 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 */
}

公共区域

位于同一边界内的元素会被视为一组。

将元素放入可视容器(卡片、方框、带边框的区域)中,即传达了”它们属于一体”的信号。

连续性

视线会沿着路径、线条和曲线自然延伸。

利用对齐与视觉流引导用户的注意力贯穿整个界面。

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
└────────────────────────────────┘

闭合性

大脑会自动补全不完整的形状。

用户不需要看到每一个像素——他们会在心中补全熟悉的形状。这使得更简洁、更精致的设计成为可能。

/* 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

字体排印是界面设计的基石。文字承载着功能、层级与品牌。糟糕的排印让界面更难用;出色的排印则浑然天成——用户甚至察觉不到它的存在。

字号比例

一致的比例尺创造视觉和谐。建议采用数学比率。

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 */
}

行高(行距)

行高对可读性有极大影响。不同内容需要不同的行距设置。

内容类型 行高 原因
标题 1.1 - 1.2 紧凑、醒目、篇幅短
UI 文字 1.3 - 1.4 标签、按钮
正文 1.5 - 1.7 提升段落可读性
长文 1.7 - 2.0 文章、技术文档

行宽(版面宽度)

合适的行宽可以减轻视觉疲劳,提升阅读理解力。

  • 最佳范围: 每行 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)

模式切换应彻底变换 UI,而非简单地显隐元素。Halide 的自动模式和手动模式是截然不同的两套界面。Warp 通过四种输入方式(键入、命令面板、AI、鼠标)在CLI和 GUI 之间架起桥梁,而不强迫用户只用一种范式。

结构化内容(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 设计模式

现代 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(设计令牌)系统

一套完整的令牌系统,确保应用全局的一致性。

: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 提取工作流

将设计文件转化为生产代码,需要系统性地提取设计令牌(design tokens)——包括颜色、排版、间距和效果,这些共同构成了设计语言的基础。

Figma Variables 导出

Figma 原生的 Variables 功能提供了最简洁的提取路径:

导出步骤: 1. 打开 Figma 文件 → 进入 Local Variables 面板 2. 点击集合菜单 → 选择”Export to JSON” 3. 另存为 figma-variables.json

JSON 令牌结构:

{
  "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" }
  }
}

令牌到 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;
}

深色模式令牌:

@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);
  }
}

令牌到 SwiftUI 的转换

颜色扩展:

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 包含浅色和深色模式
排版 样式导出 字体、字号、字重、行高
间距 Variables JSON 需标注基础单位
图标 SVG 描边样式、单色
图片 PNG @2x/@3x 或 WebP 含压缩处理
组件 Figma 链接 供开发实现时参考

质量门禁标准:

  • [ ] 所有颜色均定义为变量(无硬编码十六进制值)
  • [ ] 排版使用已定义的文本样式
  • [ ] 间距遵循网格系统(8px 基准)
  • [ ] 已提供深色模式变体
  • [ ] 交互状态已标注(hover、active、disabled)
  • [ ] 响应式断点已标注
  • [ ] 无障碍要求已注明(对比度比值)

开发者将收到:

  1. 令牌文件(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

间距比例

令牌 用途
–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 基准)

交互

  • [ ] 用户能否无需思考工具本身即可完成目标?
  • [ ] 界面是否适应当前上下文?
  • [ ] 工具是否仅在相关时才可见?
  • [ ] 反复使用是否能引导用户掌握更快捷的操作方式?

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 — 设计令牌管理工具

设计系统: - 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 设计

本指南在实践中不断完善。设计原则历久弥新,但其应用方式随技术发展与认知深化而持续演进。