软件工程师设计原则:完整指南
掌握视觉设计基础以构建更好的界面。格式塔心理学、排版、色彩理论、间距系统、动画原则,以及Dieter Rams等设计大师的教训。包含16个优秀产品的案例研究。
title: “软件工程师的设计原则:完整指南” description: “掌握视觉设计基础,构建更优秀的界面。格式塔心理学、排版、色彩理论、间距系统、动画原则,以及来自 Dieter Rams 等设计大师的经验。包含 16 个优秀产品的案例研究。” date: 2026-01-14 updated: 2026-01-17T13:30:00 author: Blake Crosley category: Design & Development tags: [Design, UI, UX, Typography, Color Theory, Gestalt, Animation, CSS, SwiftUI] url_slugs: - design-principles-software-engineers-complete-guide-2026 - visual-design-fundamentals-developers
软件工程师设计原则:完整指南
更新于 2026 年 1 月 17 日
2026 年 1 月更新: 本指南整合了永恒的设计原则与现代 Web 和 iOS 开发的实现模式。内容涵盖格式塔心理学、排版系统、色彩理论、视觉层次、间距和动画——以及来自 Dieter Rams 的设计理念。请参阅设计研究深入了解 16 个优秀产品。
多年来,我在构建软件的同时研究设计,从 Dieter Rams 等大师身上汲取原则,并深入分析 Linear、Stripe 和 Raycast 等产品的界面。本指南将这些理解提炼为我在开始关注软件外观和体验时希望拥有的完整参考。
设计不是装饰,而是沟通。每个像素都在传达功能、层次和含义。业余软件和专业软件之间的区别,在于理解这些原则并持续应用它们。
本指南假设你已经能够编写代码。它教你学会”看”——理解为什么有些界面让人感觉轻松自如,而另一些则显得混乱不堪,更重要的是,如何构建前者。
目录
第一部分:基础
第二部分:设计哲学
第三部分:实现
第四部分:参考
格式塔心理学
“整体不同于部分之和。” — Kurt Koffka
格式塔心理学起源于 1920 年代的德国,解释了人类如何感知视觉信息。大脑不会看到单独的像素——它会将元素组织成有意义的模式。掌握这些原则来控制用户如何感知你的界面。
接近性
彼此靠近的元素会被感知为一组。
这是 UI 设计中最强大的格式塔原则。空间比任何其他视觉属性都更能传达关系。
错误(等距 = 无分组):
┌─────────────────┐
│ Label │
│ │
│ Input Field │
│ │
│ Label │
│ │
│ Input Field │
└─────────────────┘
正确(不等距 = 清晰分组):
┌─────────────────┐
│ Label │
│ Input Field │ ← 紧凑 (4px) - 相关联
│ │
│ │ ← 宽松 (24px) - 分隔组
│ Label │
│ Input Field │ ← 紧凑 (4px) - 相关联
└─────────────────┘
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
相似性
具有相同视觉特征的元素看起来是相关的。
当元素看起来相同时,用户会假设它们的功能也相同。这就是为什么设计系统要使用一致的按钮样式、卡片处理和排版。
示例导航:
┌───────────────────────────────────┐
│ [Dashboard] [Projects] [Settings] │ ← 相同样式 = 相同功能
│ │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │Card │ │Card │ │Card │ │ ← 相同样式 = 相同内容类型
│ └─────┘ └─────┘ └─────┘ │
│ │
│ [+ New Project] │ ← 不同样式 = 不同功能
└───────────────────────────────────┘
图形-背景
内容应该与背景明确分离。
大脑需要区分”图形”(要关注的内容)和”背景”(底层背景)。糟糕的图形-背景关系会造成视觉混乱。
技巧: - 对比(浅色图形在深色背景上,反之亦然) - 阴影(将图形提升到背景之上) - 边框(勾勒图形边缘) - 模糊(模糊背景,锐化图形)
/* 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 */
}
共同区域
在边界内的元素会被感知为一组。
将元素包围在视觉容器(卡片、方框、边框区域)内表示它们属于一起。
连续性
眼睛会沿着路径、线条和曲线移动。
使用对齐和视觉流引导注意力穿过你的界面。
对齐中的连续性:
┌────────────────────────────────┐
│ Logo [Nav] [Nav] [Nav] │ ← 在水平轴上对齐
├────────────────────────────────┤
│ │
│ Headline │
│ ───────────────────────────── │ ← 眼睛沿着左边缘移动
│ Paragraph text continues │
│ along the same left edge │
│ │
│ [Primary Action] │ ← 仍在左边缘
└────────────────────────────────┘
闭合性
大脑会补全不完整的形状。
用户不需要绘制每个像素——他们会在脑海中补全熟悉的形状。这允许更简约、优雅的设计。
/* 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 */
}
格式塔快速参考
| 原则 | 规则 | 主要用途 |
|---|---|---|
| 接近性 | 相关 = 靠近,无关 = 远离 | 表单字段、内容区块 |
| 相似性 | 相同外观 = 相同功能 | 按钮、卡片、导航 |
| 图形-背景 | 清晰的层次分离 | 卡片、模态框、覆盖层 |
| 共同区域 | 边界组织内容 | 设置区块、用户卡片 |
| 连续性 | 沿着线条和对齐移动 | 时间线、阅读流程 |
| 闭合性 | 大脑补全形状 | 图标、滚动提示、骨架屏 |
排版
“排版是赋予人类语言持久视觉形式的技艺。” — 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 | 不适用 | 不适用 |
| 对齐 | 左对齐 | 居中可以 | 左对齐 |
色彩理论
“色彩是一种直接影响灵魂的力量。” — 瓦西里·康定斯基
色彩的传达速度超越文字。它建立情绪、引导注意力、传递意义,并构建品牌识别。
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 按钮、活泼品牌 |
| 黄色 | 乐观、警示 | 警告、高亮 |
| 紫色 | 奢华、创意 | 高端产品 |
无障碍对比度
| 级别 | 正常文本 | 大号文本 | UI 组件 |
|---|---|---|---|
| AA | 4.5:1 | 3:1 | 3:1 |
| AAA | 7:1 | 4.5:1 | 不适用 |
工具: WebAIM Contrast Checker、Chrome DevTools 颜色选择器
视觉层次
“设计是品牌无声的代言人。” — 保罗·兰德
视觉层次控制用户首先看到什么、其次看到什么、第三看到什么。没有清晰的层次,用户必须费力寻找信息。有了层次,界面就会感觉毫不费力。
层次的六种工具
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 型模式(内容页面): Z 型模式(落地页):
████████████████████████ 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); }
眯眼测试
眯起眼睛看你的设计。你还能看到层次吗?如果能,那就是强层次。
间距与节奏
“留白就像空气:它是设计呼吸所必需的。” — 沃伊切赫·齐林斯基
间距是设计的隐形结构。一致的间距创造视觉节奏——让元素感觉属于一个连贯系统的感觉。
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 |
动画原则
“动画不是会动的图画艺术,而是被绘制出来的动作艺术。” — 诺曼·麦克拉伦
动画让界面焕发生机。做得好,它引导注意力、传达状态、建立情感连接。做得不好,它令人沮丧和分心。
核心原则
动画应该感觉自然而然,而非装饰性的。
好的动画: 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 | 大型移动(页面过渡、模态框) | 从容 |
性能:黄金法则
只动画 transform 和 opacity — 这些是 GPU 加速的,不会触发布局。
/* BAD: Animating layout */
.panel { transition: left 0.3s, width 0.3s; }
/* GOOD: Using transform */
.panel { transition: transform 0.3s; }
何时不使用动画
-
用户启用了
prefers-reduced-motioncss @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } -
动画不传达任何信息 — 无意义的旋转器、弹跳元素
- 用户很匆忙 — 错误状态、表单验证、搜索结果
- 动画会减慢重复操作 — 键盘快捷键应跳过动画
动画快速参考
: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);
}
迪特·拉姆斯:十项原则
“少,但更好。” — 迪特·拉姆斯
迪特·拉姆斯是 20 世纪最具影响力的工业设计师。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. 好的设计是持久的 避免很快过时的趋势。经典优于时髦。
时髦的(会过时): 永恒的:
- 极端毛玻璃效果 - 干净的排版
- 霓虹色、故障效果 - 微妙的层级
- 激进的渐变 - 中性色系配以深思熟虑的强调色
8. 好的设计注重每一个细节 没有任何东西是随意的。加载状态、空状态、错误状态——都要经过设计。
9. 好的设计是环保的 性能就是环保。尊重用户的注意力。高效的代码。
10. 好的设计是尽可能少的设计 移除所有不必要的东西。最好的设计是不可见的。
2025 年 Web 设计模式
现代 Web 设计利用原生 CSS 功能,在许多情况下无需 JavaScript。
容器查询
根据容器而非视口调整组件大小。
.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>
设计令牌系统
一套完整的令牌系统,确保应用程序的一致性。
: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 提取工作流
将设计文件转换为生产代码需要系统地提取设计令牌——颜色、排版、间距和效果,这些定义了你的设计语言。
Figma 变量导出
Figma 原生的变量功能提供了最简洁的提取路径:
导出步骤:
1. 打开 Figma 文件 → 本地变量面板
2. 点击集合菜单 → “导出为 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 | 包含浅色和深色模式 |
| 排版 | Styles 导出 | 字体、大小、粗细、行高 |
| 间距 | Variables JSON | 记录基础单位 |
| 图标 | SVG | 描边,单色 |
| 图片 | PNG @2x/@3x 或 WebP | 带压缩 |
| 组件 | Figma 链接 | 供实现时参考 |
质量关卡标准:
- [ ] 所有颜色定义为变量(无硬编码十六进制值)
- [ ] 排版使用定义的文本样式
- [ ] 间距遵循网格系统(8px 基准)
- [ ] 提供深色模式变体
- [ ] 记录交互状态(悬停、激活、禁用)
- [ ] 标注响应式断点
- [ ] 注明无障碍要求(对比度)
开发者接收:
- 令牌文件(JSON/CSS/Swift,取决于平台)
- 带有尺寸的组件规格
- 所需格式的资源导出
- 交互文档(状态、动画)
- 无障碍标注
快速参考表
格式塔原则
| 原则 | 规则 | 用途 |
|---|---|---|
| 邻近性 | 相关 = 靠近 | 表单、区块 |
| 相似性 | 外观相同 = 功能相同 | 按钮、卡片 |
| 图底关系 | 清晰的层次分离 | 模态框、卡片 |
| 连续性 | 沿线条流动 | 时间线、对齐 |
| 闭合性 | 大脑会补全形状 | 图标、滚动提示 |
排版
| 元素 | 大小 | 粗细 | 行高 |
|---|---|---|---|
| 正文 | 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 | 页面区块 |
设计检查清单
在发布任何界面之前,请验证:
格式塔
- [ ] 相关元素比不相关元素更靠近(邻近性)
- [ ] 相似功能具有相似样式(相似性)
- [ ] 前景和背景之间有清晰的分离(图底关系)
- [ ] 视线自然流动通过布局(连续性)
排版
- [ ] 基础字号至少为 16px
- [ ] 正文行高为 1.5 以上
- [ ] 行长度不超过 75 个字符
- [ ] 层次清晰(可区分 3 个级别)
- [ ] 全局使用一致的比例
颜色
- [ ] 所有文本通过 4.5:1 对比度(WCAG AA)
- [ ] 颜色不是唯一的指示器(还有图标/标签)
- [ ] 有意识地设计深色模式
- [ ] 遵循 60-30-10 分配原则
视觉层次
- [ ] 能够识别出最重要的第一元素
- [ ] 视线按预期顺序流动
- [ ] 每个区块有一个清晰的 CTA
- [ ] 字体比例一致
间距
- [ ] 所有间距使用定义的比例(无魔术数字)
- [ ] 卡片/组件具有一致的内边距
- [ ] 移动端间距舒适
- [ ] 网格对齐一致(8px 基准)
Dieter Rams 检查
- [ ] 还能删除什么?
- [ ] 每个元素都有功能吗?
- [ ] 5 年后这会显得过时吗?
- [ ] 我设计了每个状态吗?
资源
书籍: - Sophie Lovell 著 As Little Design as Possible(关于 Dieter Rams) - Robert Bringhurst 著 The Elements of Typographic Style
工具: - 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 | 乐观 UI、键盘优先工作流 |
| Raycast | 扩展系统、快捷操作 |
iOS 原生应用(Apple Design Award 获奖作品)
| 产品 | 核心贡献 |
|---|---|
| Flighty | 15 种智能状态、实时活动、数据可视化 |
| Halide | 智能激活、手势控制 |
| Bear | 排版优先、内联标签 |
| Craft | 原生优先跨平台、嵌套页面 |
| Things | 延期日期、快速输入模式 |
生产力与 AI
| 产品 | 核心贡献 |
|---|---|
| Superhuman | 100ms 法则、命令面板训练、练习式引导 |
| Perplexity | 引用优先的 AI、流式输出阶段 |
| Notion | 块系统、斜杠命令 |
| Arc | 空间、分屏视图、命令栏 |
| Stripe | 卓越文档、API 设计 |
本指南在实践中不断完善。设计原则是永恒的,但其应用随技术和认知的发展而演进。