Guide / 15 min

软件工程师的设计原则

学习构建更好软件的设计基础。排版、色彩理论、间距、动画,以及从Arc到Stripe的16个产品案例研究。

更新于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 心理学发展于20世纪20年代的德国,它解释了人类如何感知视觉信息。大脑并不是逐个像素地观看——而是将元素组织成有意义的模式。掌握这些原则,您就能控制用户如何感知您的界面。

邻近性

彼此靠近的元素会被感知为一组。

这是 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% - Dominant (Background)
│░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 30% - Secondary (Cards, sections)
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░│
│░░░░░▓▓▓▓▓▓▓▓▓▓▓▓██████▓▓▓▓▓▓▓▓▓▓░░░░░░░░│ 10% - Accent (Buttons, links)
└──────────────────────────────────────────┘

构建色彩体系

每个界面都需要以下语义颜色:

: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. 预备动作 — 为用户即将发生的事做好准备。

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

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

2. 跟随动作 — 让运动以弹性回弹的方式自然完成。

.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-out 进入元素 快速启动,缓慢停止
ease-in 退出元素 缓慢启动,快速退出
ease-in-out 状态切换 全程顺滑
linear 加载指示器 持续、机械式

4. 引导聚焦 — 将注意力引向重要的内容。除非作为编排好的群组动画,否则同一时间只应有一个元素在运动。

5. 交错出现 — 元素应依次到达,而非同时出现。

.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世纪最具影响力的工业设计师。作为 Braun 1961年至1995年的设计总监,他创造了数十年后依然经典的产品。他的作品直接启发了 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系统

一套完整的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提取工作流

将设计文件转化为生产代码,需要系统性地提取design tokens——即定义设计语言的颜色、排版、间距和效果。

Figma变量导出

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

导出步骤: 1. 打开Figma文件 → 本地变量面板 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的转换

颜色扩展:

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为基准)
  • [ ] 提供深色模式变体
  • [ ] 交互状态已标注(悬停、激活、禁用)
  • [ ] 响应式断点已注明
  • [ ] 无障碍访问要求已标注(对比度比值)

开发者接收的内容:

  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 — Design 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 设计

本指南随实践不断丰富。设计原则是永恒的,但其应用方式随着技术与认知的发展而不断演进。