🌍 语言 · 规范文档为英文 · English · Español · Português (Brasil) · 日本語 · 한국어 · Русский
快速统一文本、按钮和表面样式,构建一致的设计系统。
Text("简洁的修饰符")
.gentleText(.title_xl)
Button("贯穿整个应用") { }
.gentleButton(.primary)
VStack {
Text("节省大量时间")
}
.gentleSurface(.card)希望在 SwiftUI 上建立坚实基础并支持长期主题演进的应用。
实际体验: 打开 Demo/GentleDesignSystemDemo.xcodeproj 来探索各个组件。示例应用还支持通过系统共享面板编辑和共享 JSON 规格。
.package(url: "https://github.com/gentle-giraffe-apps/GentleDesignSystem.git", from: "0.1.7")import GentleDesignSystem
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
GentleThemeRoot(theme: .default) {
ContentView()
}
}
}
}Text("欢迎")
.gentleText(.title_xl)
Text("描述")
.gentleText(.body_m, colorRole: .textSecondary)Button("继续") { }
.gentleButton(.primary)
Button("取消") { }
.gentleButton(.secondary)VStack {
Text("卡片内容")
}
.gentleSurface(.card)CI、静态分析和覆盖率详情
本项目通过 CI 和静态分析实施质量管控:
- CI: 所有提交到
main的代码必须通过 GitHub Actions 检查 - 静态分析: DeepSource 在每次提交时运行
- 测试覆盖率: Codecov 报告行覆盖率
GentleDesignSystem 围绕三个层次精心设计:
- 令牌定义(Codable,兼容 JSON)
- 运行时解析(主题 + Environment)
- SwiftUI 易用性(修饰符与扩展)
这种分层保持了设计意图的清晰、运行时行为的可预测以及未来演进的安全。
系统架构(图表)
flowchart TB
subgraph Tokens["令牌层(设计时)"]
Spec[GentleDesignSystemSpec]
Spec --> Colors[GentleColorTokens]
Spec --> Typography[GentleTypographyTokens]
Spec --> Layout[GentleLayoutTokens]
Spec --> Visual[GentleVisualTokens]
Spec --> Buttons[GentleButtonTokens]
Spec --> Surfaces[GentleSurfaceTokens]
end
subgraph Runtime["运行时层"]
Theme[GentleTheme]
Manager[GentleThemeManager]
Store[GentleFileThemeSpecStore]
Manager --> Theme
Store -.->|加载/保存| Manager
end
subgraph SwiftUI["SwiftUI 层"]
Root[GentleThemeRoot]
Env[Environment Values .gentleTheme]
Modifiers[视图修饰符]
Root --> Env
Env --> Modifiers
end
Tokens --> Runtime
Runtime --> SwiftUI
数据流(图表)
flowchart TB
JSON[(JSON 文件)] -->|加载| Store[GentleFileThemeSpecStore]
Store --> Manager[GentleThemeManager]
Manager --> Theme[GentleTheme]
Theme --> Resolve{解析}
Resolve -->|ColorScheme| ResolvedColor[颜色]
Resolve -->|ContentSizeCategory| ResolvedFont[字体]
ResolvedColor --> View[SwiftUI 视图]
ResolvedFont --> View
View -->|.gentleText| Text
View -->|.gentleButton| Button
View -->|.gentleSurface| Surface
数据模型(规格结构)
设计系统由单一的 JSON 兼容规格定义
(GentleDesignSystemSpec)。
GentleDesignSystemSpec
│
├── colors: GentleColorTokens
│ │
│ └── pairByRole: [String: GentleColorPair]
│ │
│ ├── 键 = GentleColorRole.rawValue
│ └── 值 = GentleColorPair
│ ├── lightHex: String
│ └── darkHex: String
│
├── typography: GentleTypographyTokens
│ │
│ └── roles: [String: GentleTypographyRoleSpec]
│ │
│ ├── 键 = GentleTextRole.rawValue
│ └── 值 = GentleTypographyRoleSpec
│ ├── pointSize: Double
│ ├── weight: GentleFontWeightToken
│ ├── design: GentleFontDesignToken
│ ├── width: GentleFontWidthToken?
│ ├── relativeTo: GentleFontTextStyle
│ ├── lineSpacing: Double
│ ├── letterSpacing: Double
│ ├── isUppercased: Bool
│ └── colorRole: GentleColorRole
│
├── layout: GentleLayoutTokens
│ │
│ ├── scale: GentleSpacingScaleTokens
│ │ ├── xs / s / m / l / xl / xxl : Double
│ │ └── value(for: GentleSpacingToken) -> Double
│ │
│ ├── gap: GentleGapTokens
│ ├── grid: GentleGridSpacingTokens
│ ├── touch: GentleTouchTokens
│ │
│ └── inset: GentleInsetTokens
│ │
│ └── tokensByRole: [String: GentleAxisInsetTokens]
│ │
│ ├── 键 = GentleInsetRole.rawValue
│ └── 值 = GentleAxisInsetTokens
│ ├── horizontal: GentleSpacingToken
│ └── vertical: GentleSpacingToken
│
├── visual: GentleVisualTokens
│ │
│ ├── radii: GentleRadiusTokens
│ │ ├── small: Double
│ │ ├── medium: Double
│ │ ├── large: Double
│ │ └── pill: Double
│ │
│ └── shadows: GentleShadowTokens
│ ├── none: Double
│ ├── small: Double
│ └── medium: Double
│
├── buttons: GentleButtonTokens
│ │
│ ├── roles: [String: GentleButtonRoleSpec]
│ │ │
│ │ ├── 键 = GentleButtonRole.rawValue
│ │ └── 值 = GentleButtonRoleSpec
│ │ ├── shape: GentleButtonShape
│ │ ├── fillRole: GentleButtonFillRole
│ │ ├── borderRole: GentleButtonBorderRole
│ │ ├── animationRole: GentleButtonAnimationRole
│ │ ├── pressedScale: Double
│ │ ├── pressedOpacity: Double
│ │ └── usesNativeStyle: Bool
│ │
│ └── animations: [String: GentleButtonAnimationSpec]
│ │
│ ├── 键 = GentleButtonAnimationRole.rawValue
│ └── 值 = GentleButtonAnimationSpec
│ ├── pressedScale: Double
│ ├── pressedOpacity: Double
│ ├── duration: Double
│ ├── springResponse: Double
│ ├── springDamping: Double
│ └── springBlend: Double
│
└── surfaces: GentleSurfaceTokens
│
└── roles: [String: GentleSurfaceRoleSpec]
│
├── 键 = GentleSurfaceRole.rawValue
└── 值 = GentleSurfaceRoleSpec
├── backgroundStyle: GentleSurfaceBackgroundStyle
│ ├── .solid(colorRole: GentleColorRole)
│ ├── .material(material:, tintColorRole:, tintOpacity:)
│ └── .glass(fallbackMaterial:, fallbackColorRole:)
├── border: GentleColorPair
├── cornerRadius: Double
├── borderWidth: Double
├── shadowRadius: Double
├── shadowOpacity: Double
├── shadowOffsetX: Double
└── shadowOffsetY: Double
为什么使用角色而非直接值? 角色提供稳定的标识符,使主题可以安全地随时间演进。 规格可以更改,预设可以替换,值可以覆盖,而不会 破坏调用位置或序列化的主题。
令牌层定义了你的设计系统意味着什么——而不是如何渲染。
| 类别 | 类型 |
|---|---|
| 排版 | GentleTextRole, GentleTypographyRoleSpec, GentleTypographyTokens |
| 颜色 | GentleColorRole, GentleColorPair, GentleColorTokens |
| 布局 | GentleLayoutTokens, GentleSpacingToken, GentleGapTokens, GentleInsetTokens |
| 视觉 | GentleVisualTokens, GentleRadiusTokens, GentleShadowTokens |
| 按钮 | GentleButtonRole, GentleButtonRoleSpec, GentleButtonTokens, GentleButtonAnimationRole |
| 表面 | GentleSurfaceRole, GentleSurfaceRoleSpec, GentleSurfaceTokens |
令牌保证与基础规格
所有令牌都是:
CodableSendable- 兼容 JSON
这使得以下操作变得容易:
- 持久化主题
- 远程加载主题
- 日后跨平台共享令牌
public struct GentleDesignSystemSpec: Codable, Sendable {
public var specVersion: String
public var colors: GentleColorTokens
public var typography: GentleTypographyTokens
public var layout: GentleLayoutTokens
public var visual: GentleVisualTokens
public var buttons: GentleButtonTokens
public var surfaces: GentleSurfaceTokens
}默认主题(.default)只是一个具体的规格。
在运行时,令牌被解析为实际的 SwiftUI 值。
GentleTheme:
- 拥有一个
GentleDesignSystemSpec - 解析:
- 根据
ColorScheme解析颜色 - 根据
ContentSizeCategory(动态字体)解析字体
- 根据
@Environment(\.gentleTheme) var theme排版解析使用 UIFontMetrics 来正确缩放自定义字体大小,同时保持与 Apple 语义文本样式的锚定。
这确保了:
- 辅助功能缩放正常工作
- 自定义点大小保持比例
- 未来的动态字体更改保持安全
运行时访问辅助工具
// 访问已解析的主题值
@GentleDesignRuntime private var design
// 在视图中使用
design.color(.textPrimary) // 当前配色方案的颜色
design.layout.stack.regular // CGFloat 间距值
design.buttons // 按钮令牌SwiftUI 的 environment 是自上而下流动的。
通过用以下方式包装应用根视图:
GentleThemeRoot {
ContentView()
}你可以确保:
- 所有子视图接收相同的主题
- 预览行为一致
- 后续主题覆盖很容易(按场景、按功能、按预览)
GentleThemeRoot 被设计得非常轻量——它只注入一个 environment 值。
这避免了:
- 全局单例
- 静态状态
- 隐式的魔法
GentleDesignSystem 提供符合人体工程学的 API,同时保持逻辑集中。
文本修饰符
Text("你好")
.gentleText(.headline_m)内部实现:
- 通过
GentleTheme解析排版 - 应用字体、宽度、设计、间距、颜色
- 自动适配动态字体
表面
VStack { ... }
.gentleSurface(.card)表面应用:
- 背景颜色
- 内边距(适当时)
- 圆角
- 边框或阴影
基于角色的 API 避免了"魔法数字"渗透到视图中。
按钮
Button("保存") { }
.gentleButton(.primary)按钮具有以下特点:
- 通过
ButtonStyle设置样式 - 完全由主题驱动
- 支持可配置的动画
- 易于扩展新角色
运行时编辑、持久化和存储
GentleThemeManager
@main
struct MyApp: App {
@State private var manager = GentleThemeManager(theme: .default)
var body: some Scene {
WindowGroup {
GentleThemeRoot(theme: manager.theme) {
ContentView()
}
.environment(\.gentleThemeManager, manager)
}
}
}使用 Manager
@GentleThemeManagerRuntime private var manager
// 将当前主题保存到磁盘
try manager.save()
// 加载已持久化的主题
try manager.load()
// 获取用于编辑的绑定
manager.typographyBinding(for: .body_m)
manager.colorBinding(for: .primaryCTA)持久化
GentleFileThemeSpecStore 处理 JSON 到 Application Support 的持久化:
let store = GentleFileThemeSpecStore(fileName: "my-theme.json")
let manager = GentleThemeManager(theme: .default, store: store)GentleDesignSystem 包含 9 个内置主题预设,每个都针对不同的使用场景和审美风格设计。
可用的主题预设
// 获取所有可用预设
let presets = GentleDesignSystemSpec.allPresets
// 每个预设提供:
// - name:显示名称(如 "Gentle Default")
// - summary:简短标语
// - description:详细说明
// - purpose:何时使用此预设
// - systemImageString:用于 UI 的 SF Symbol 名称
// - spec:实际的 GentleDesignSystemSpec| 预设 | 摘要 | 最适用于 |
|---|---|---|
| Gentle Default | 平静、均衡的基础 | 层次清晰的通用起点 |
| Classic Tan | 温暖、经典的大地色调 | 需要温馨感和传统感的应用 |
| Modern Gray | 简约、现代的中性基底 | 以清晰度为核心的商务应用 |
| Soft Green | 清新、自然的舒缓色调 | 健康、效率、专注类应用 |
| Editorial Paper | 精致的印刷风格阅读体验 | 内容密集型应用、长文阅读 |
| Technical Blue | 精确、可信赖的蓝色调 | 开发者工具、仪表盘 |
| Bold Orange | 充满活力、能量满满 | 激发行动力的应用 |
| Elegant Purple | 优雅、奢华的丰富色调 | 生活方式、创意、高端应用 |
| Compact Mint | 紧凑、高效的清新色调 | 数据密集型界面 |
// 将预设应用到主题管理器
@GentleThemeManagerRuntime private var manager
// 查找并应用预设
if let editorialPreset = GentleDesignSystemSpec.allPresets.first(where: { $0.name == "Editorial Paper" }) {
manager.theme.editableSpec = editorialPreset.spec
}示例应用包含一个 ThemePickerView,将所有预设显示为可交互的卡片。每张卡片使用预设自身的主题来预览其排版和颜色:
构建主题选择器
ForEach(presets, id: \.name) { preset in
let previewTheme = GentleTheme(
defaultSpec: preset.spec,
editableSpec: preset.spec
)
Button {
themeManager.theme.editableSpec = preset.spec
} label: {
GentleThemeRoot(theme: previewTheme) {
// 卡片内容使用预设自身的样式渲染
ThemePresetCard(preset: preset)
}
}
}排版角色
17 个语义化文本角色,按大小梯度排列(xxl > xl > l > ml > m > ms > s):
| 梯度 | 角色 |
|---|---|
| XXL | largeTitle_xxl |
| XL | title_xl |
| L | title2_l |
| ML | title3_ml |
| M | headline_m, body_m, bodySecondary_m, monoCode_m, primaryButtonTitle_m, secondaryButtonTitle_m, tertiaryButtonTitle_m, quaternaryButtonTitle_m |
| MS | callout_ms, subheadline_ms |
| S | footnote_s, caption_s, caption2_s |
每个角色解析为 GentleTypographyRoleSpec,包含:pointSize、weight、design、width、relativeTo、lineSpacing、letterSpacing、isUppercased 和 colorRole。
按钮角色与动画
按钮角色
primary · secondary · tertiary · quaternary · destructive
按钮动画
| 动画 | 描述 |
|---|---|
unknown |
无动画 |
subtlePress |
微妙的按压反馈 |
squish |
按压时的挤压效果 |
pop |
弹跳效果 |
bouncy |
弹性弹簧动画 |
springBack |
按压时缩小,回弹超过原始大小后稳定 |
表面角色
`appBackground` · `card` · `cardElevated` · `cardSecondary` · `chrome` · `overlaySheet` · `overlayPopover` · `overlayScrim` · `floatingPanel` · `floatingWidget`颜色角色
| 类别 | 角色 |
|---|---|
| 文本 (9) | textPrimary, textSecondary, textTertiary, textOnPrimaryCTA, textOnDestructive, textOnOverlay, textOnOverlaySecondary, textOnScrim, textOnScrimSecondary |
| 表面 (6) | background, surfaceBase, surfaceCardSecondary, surfaceTint, surfaceScrim, borderSubtle |
| 操作 (2) | primaryCTA, destructive |
| 主题 (2) | themePrimary, themeSecondary |
使用语义分组:GentleColorRole.textRoles、.surfaceRoles、.actionRoles、.themeRoles
使用成员检查:role.isTextRole、.isSurfaceRole、.isActionRole、.isThemeRole
间距与圆角令牌
间距令牌
xs (4) · s (8) · m (12) · l (16) · xl (24) · xxl (32)
圆角令牌
small (8) · medium (12) · large (20) · pill (999)
- iOS 18.0+
- Swift 6.1+
本仓库中的部分起草和编辑润色工作借助了大语言模型(包括 ChatGPT、Claude 和 Gemini)进行加速,所有工作均在人工直接设计、验证和最终审批下完成。所有技术决策、代码和架构结论均由仓库维护者编写和验证。



