@deernova/style 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/bin/cli.mjs +212 -32
- package/package.json +1 -1
- package/skill/SKILL.md +90 -22
- package/skill/assets/DeernovaTokens.swift +376 -0
- package/skill/assets/screens/iphone-button-sizes-dark.png +0 -0
- package/skill/assets/screens/iphone-button-sizes-light.png +0 -0
- package/skill/assets/screens/iphone-screens-dark.png +0 -0
- package/skill/assets/screens/iphone-screens-light.png +0 -0
- package/skill/assets/screens/mac-window-dark.png +0 -0
- package/skill/assets/screens/mac-window-light.png +0 -0
- package/skill/assets/tailwind.css +106 -0
- package/skill/assets/tokens.css +533 -0
- package/skill/assets/tokens.json +1686 -0
- package/skill/manifest.json +2 -22
- package/skill/references/components/banner.md +95 -0
- package/skill/references/components/button.md +181 -0
- package/skill/references/components/card.md +89 -0
- package/skill/references/components/code-block.md +77 -0
- package/skill/references/components/composer.md +206 -0
- package/skill/references/components/empty-state.md +63 -0
- package/skill/references/components/glass-controls.md +127 -0
- package/skill/references/components/list.md +130 -0
- package/skill/references/components/message.md +94 -0
- package/skill/references/components/segmented.md +79 -0
- package/skill/references/components/sheet.md +84 -0
- package/skill/references/components/sidebar.md +139 -0
- package/skill/references/components/step-row.md +135 -0
- package/skill/references/components/tag.md +86 -0
- package/skill/references/components/task-list.md +83 -0
- package/skill/references/components/text-field.md +86 -0
- package/skill/references/components/toolbar.md +139 -0
- package/skill/references/foundations/accessibility.md +27 -0
- package/skill/references/foundations/color.md +58 -0
- package/skill/references/foundations/elevation.md +17 -0
- package/skill/references/foundations/glass.md +36 -0
- package/skill/references/foundations/iconography.md +44 -0
- package/skill/references/foundations/motion.md +22 -0
- package/skill/references/foundations/radius.md +31 -0
- package/skill/references/foundations/spacing.md +38 -0
- package/skill/references/foundations/stroke.md +35 -0
- package/skill/references/foundations/typography.md +54 -0
- package/skill/references/foundations/writing.md +10 -0
- package/skill/references/review.md +26 -13
- package/skill/references/scenes/ios.md +61 -0
- package/skill/references/scenes/ipados.md +42 -0
- package/skill/references/scenes/macos.md +65 -0
- package/skill/references/scenes/swiftui.md +94 -0
- package/skill/references/scenes/web-marketing.md +34 -0
- package/skill/references/scenes/web.md +83 -0
- package/skill/references/tokens.md +441 -74
- package/skill/assets/design/README.md +0 -170
- package/skill/assets/design/components/Banner/README.md +0 -33
- package/skill/assets/design/components/Banner/preview.html +0 -26
- package/skill/assets/design/components/Button/README.md +0 -59
- package/skill/assets/design/components/Button/preview.html +0 -27
- package/skill/assets/design/components/Card/README.md +0 -31
- package/skill/assets/design/components/Card/preview.html +0 -24
- package/skill/assets/design/components/CodeBlock/README.md +0 -34
- package/skill/assets/design/components/CodeBlock/preview.html +0 -16
- package/skill/assets/design/components/Composer/README.md +0 -42
- package/skill/assets/design/components/Composer/preview.html +0 -28
- package/skill/assets/design/components/Cover/preview.html +0 -53
- package/skill/assets/design/components/EmptyState/README.md +0 -28
- package/skill/assets/design/components/EmptyState/preview.html +0 -16
- package/skill/assets/design/components/GlassControls/README.md +0 -43
- package/skill/assets/design/components/GlassControls/preview.html +0 -40
- package/skill/assets/design/components/ListGroup/README.md +0 -39
- package/skill/assets/design/components/ListGroup/preview.html +0 -22
- package/skill/assets/design/components/MacWindow/README.md +0 -27
- package/skill/assets/design/components/MacWindow/preview.html +0 -70
- package/skill/assets/design/components/Message/README.md +0 -31
- package/skill/assets/design/components/Message/preview.html +0 -21
- package/skill/assets/design/components/PhoneScreen/README.md +0 -25
- package/skill/assets/design/components/PhoneScreen/preview.html +0 -74
- package/skill/assets/design/components/Segmented/README.md +0 -21
- package/skill/assets/design/components/Segmented/preview.html +0 -21
- package/skill/assets/design/components/Sheet/README.md +0 -29
- package/skill/assets/design/components/Sheet/preview.html +0 -31
- package/skill/assets/design/components/Sidebar/README.md +0 -36
- package/skill/assets/design/components/Sidebar/preview.html +0 -20
- package/skill/assets/design/components/StepRow/README.md +0 -35
- package/skill/assets/design/components/StepRow/preview.html +0 -17
- package/skill/assets/design/components/Tag/README.md +0 -36
- package/skill/assets/design/components/Tag/preview.html +0 -22
- package/skill/assets/design/components/TextField/README.md +0 -29
- package/skill/assets/design/components/TextField/preview.html +0 -28
- package/skill/assets/design/components/bundle.css +0 -189
- package/skill/assets/design/design-system.json +0 -25
- package/skill/assets/design/images/Banner-dark.png +0 -0
- package/skill/assets/design/images/Banner-light.png +0 -0
- package/skill/assets/design/images/Button-dark.png +0 -0
- package/skill/assets/design/images/Button-light.png +0 -0
- package/skill/assets/design/images/Card-dark.png +0 -0
- package/skill/assets/design/images/Card-light.png +0 -0
- package/skill/assets/design/images/CodeBlock-dark.png +0 -0
- package/skill/assets/design/images/CodeBlock-light.png +0 -0
- package/skill/assets/design/images/Composer-dark.png +0 -0
- package/skill/assets/design/images/Composer-light.png +0 -0
- package/skill/assets/design/images/Cover-dark.png +0 -0
- package/skill/assets/design/images/Cover-light.png +0 -0
- package/skill/assets/design/images/EmptyState-dark.png +0 -0
- package/skill/assets/design/images/EmptyState-light.png +0 -0
- package/skill/assets/design/images/Example-iOSButtonsInPhone-dark.png +0 -0
- package/skill/assets/design/images/Example-iOSButtonsInPhone-light.png +0 -0
- package/skill/assets/design/images/GlassControls-dark.png +0 -0
- package/skill/assets/design/images/GlassControls-light.png +0 -0
- package/skill/assets/design/images/ListGroup-dark.png +0 -0
- package/skill/assets/design/images/ListGroup-light.png +0 -0
- package/skill/assets/design/images/MacWindow-dark.png +0 -0
- package/skill/assets/design/images/MacWindow-light.png +0 -0
- package/skill/assets/design/images/Message-dark.png +0 -0
- package/skill/assets/design/images/Message-light.png +0 -0
- package/skill/assets/design/images/PhoneScreen-dark.png +0 -0
- package/skill/assets/design/images/PhoneScreen-light.png +0 -0
- package/skill/assets/design/images/Segmented-dark.png +0 -0
- package/skill/assets/design/images/Segmented-light.png +0 -0
- package/skill/assets/design/images/Sheet-dark.png +0 -0
- package/skill/assets/design/images/Sheet-light.png +0 -0
- package/skill/assets/design/images/Sidebar-dark.png +0 -0
- package/skill/assets/design/images/Sidebar-light.png +0 -0
- package/skill/assets/design/images/StepRow-dark.png +0 -0
- package/skill/assets/design/images/StepRow-light.png +0 -0
- package/skill/assets/design/images/Tag-dark.png +0 -0
- package/skill/assets/design/images/Tag-light.png +0 -0
- package/skill/assets/design/images/TextField-dark.png +0 -0
- package/skill/assets/design/images/TextField-light.png +0 -0
- package/skill/assets/design/tokens.css +0 -187
- package/skill/assets/design/tokens.json +0 -142
- package/skill/references/components.md +0 -648
- package/skill/references/foundations.md +0 -170
- package/skill/references/native.md +0 -7
- package/skill/references/web.md +0 -11
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# 文本框
|
|
2
|
+
|
|
3
|
+
单行文字输入,配一个标签和一句说明。
|
|
4
|
+
|
|
5
|
+
**需要提供**:标签(名词)、绑定的文本、可选的占位符(给示例,不要重复标签)、说明文字,校验失败时提供错误信息。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:text-field -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `field-height` | 28 | 44 | 40 | 单行输入框和下拉选择的高度。 |
|
|
13
|
+
| `field-padding-x` | 10 | 12 | 14 | 输入框左右内边距。 |
|
|
14
|
+
| `field-font-size` | 14 | 17 | 15 | 输入文字字号。触控端不小于 16,避免浏览器放大页面。 |
|
|
15
|
+
| `field-radius` | 6 | 8 | 8 | 输入框圆角。 |
|
|
16
|
+
<!-- /tokens -->
|
|
17
|
+
|
|
18
|
+
- 底色 `surface-raised`,边框 `stroke-control` 宽、`line-control` 色,保证 3:1。
|
|
19
|
+
- 聚焦时边框变 `focus-ring`,外圈加 3px 的 `accent-soft` 光晕。
|
|
20
|
+
- 出错时边框变 `danger`,说明文字换成错误信息,同样用 `danger` 色。错误信息要说清楚怎么改。
|
|
21
|
+
- 标签在输入框上方,`callout` 字号、中粗、`ink-secondary`;说明在下方,`caption` 字号、常规字重、`ink-tertiary`。标签、输入框、说明之间 `space-1`。
|
|
22
|
+
- 触控端输入文字不小于 16px,避免浏览器在聚焦时放大页面(`field-font-size` 已经保证)。
|
|
23
|
+
- 下拉选择(`select`)和输入框同高、同边框。
|
|
24
|
+
|
|
25
|
+
## Web
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
.dn-field { display: grid; gap: var(--space-1); min-width: 0; }
|
|
29
|
+
.dn-field-label { color: var(--ink-secondary); font: var(--weight-medium) var(--type-callout-size)/var(--type-callout-line) var(--font-sans); }
|
|
30
|
+
.dn-input {
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
width: 100%;
|
|
33
|
+
height: var(--field-height);
|
|
34
|
+
padding: 0 var(--field-padding-x);
|
|
35
|
+
border: var(--stroke-control) var(--line-style-divider) var(--line-control);
|
|
36
|
+
border-radius: var(--field-radius);
|
|
37
|
+
background: var(--surface-raised);
|
|
38
|
+
color: var(--ink);
|
|
39
|
+
font: var(--weight-regular) var(--field-font-size)/1.2 var(--font-sans);
|
|
40
|
+
transition: border-color var(--duration-instant) var(--ease-standard), box-shadow var(--duration-instant) var(--ease-standard);
|
|
41
|
+
}
|
|
42
|
+
.dn-input::placeholder { color: var(--ink-tertiary); }
|
|
43
|
+
.dn-input:focus { outline: none; border-color: var(--focus-ring); box-shadow: 0 0 0 3px var(--accent-soft); }
|
|
44
|
+
.dn-input[aria-invalid="true"] { border-color: var(--danger); }
|
|
45
|
+
.dn-input:disabled { opacity: var(--opacity-disabled); }
|
|
46
|
+
.dn-field-help { color: var(--ink-tertiary); font: var(--weight-regular) var(--type-caption-size)/var(--type-caption-line) var(--font-sans); }
|
|
47
|
+
.dn-field-help.is-error { color: var(--danger); }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<div class="dn-row is-top">
|
|
52
|
+
<div class="dn-field">
|
|
53
|
+
<label class="dn-field-label" for="email">邮箱</label>
|
|
54
|
+
<input class="dn-input" id="email" type="email" placeholder="name@example.com" aria-describedby="email-help">
|
|
55
|
+
<p class="dn-field-help" id="email-help">用于接收登录验证码。</p>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="dn-field">
|
|
58
|
+
<label class="dn-field-label" for="name">昵称</label>
|
|
59
|
+
<input class="dn-input" id="name" value="林" aria-invalid="true" aria-describedby="name-help">
|
|
60
|
+
<p class="dn-field-help is-error" id="name-help">昵称至少需要 2 个字。</p>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## SwiftUI
|
|
66
|
+
|
|
67
|
+
```swift
|
|
68
|
+
VStack(alignment: .leading, spacing: Deernova.space1) {
|
|
69
|
+
Text("邮箱").font(.deernovaCallout.weight(.medium)).foregroundStyle(.inkSecondary)
|
|
70
|
+
TextField("name@example.com", text: $email)
|
|
71
|
+
.textFieldStyle(.roundedBorder) // Mac;iOS 用自定义样式实现上面的规格
|
|
72
|
+
.textContentType(.emailAddress)
|
|
73
|
+
.keyboardType(.emailAddress) // iOS
|
|
74
|
+
Text(error ?? "用于接收登录验证码。")
|
|
75
|
+
.font(.caption)
|
|
76
|
+
.foregroundStyle(error == nil ? .inkTertiary : .danger)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
iOS 设置页这类表单,直接在 `Form` 里用 `TextField`,交给系统的分组样式,见 [分组列表](list.md)。
|
|
81
|
+
|
|
82
|
+
## 不要
|
|
83
|
+
|
|
84
|
+
- 只用占位符代替标签。
|
|
85
|
+
- 出错时只把边框变红、不给说明。
|
|
86
|
+
- 把多行输入塞进单行框(多行用 `textarea`、`TextField(axis: .vertical)` 或 [Composer](composer.md))。
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# 顶栏与侧面板
|
|
2
|
+
|
|
3
|
+
Web 应用的页面骨架:左边是[侧边栏](sidebar.md),中间是内容区(顶栏 + 对话栏 + 底部的 [Composer](composer.md)),右边可以并排打开侧面板(终端、预览、改动、文件)。macOS 和 iPadOS 上这些由系统的 `NavigationSplitView`、`.toolbar` 和 `.inspector` 提供,带系统玻璃,不用自己画。
|
|
4
|
+
|
|
5
|
+
**需要提供**:页面标题(可以点开切换或重命名)、顶栏右侧的工具按钮(图标、无障碍标签、是否打开);侧面板的标签列表和当前标签。
|
|
6
|
+
|
|
7
|
+
## 尺寸
|
|
8
|
+
|
|
9
|
+
<!-- tokens:toolbar -->
|
|
10
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
11
|
+
| --- | --- | --- | --- | --- |
|
|
12
|
+
| `toolbar-title-size` | 15 | 17 | 15 | 顶栏标题字号(常规字重)。 |
|
|
13
|
+
| `panel-tab-height` | 28 | 36 | 32 | 侧面板标签头里每个标签的高度。 |
|
|
14
|
+
<!-- /tokens -->
|
|
15
|
+
|
|
16
|
+
- **顶栏**:高 `toolbar-height`,`canvas` 底,不透明,底部一条 1px `line`,吸顶(`z-sticky`)。左边是标题(`toolbar-title-size`、常规字重、`ink`,后面一个小箭头,点开是菜单);右边是一排[图标按钮](button.md),间隔 2,打开状态的按钮用 `fill-selected` 底。
|
|
17
|
+
- **内容区**:对话栏宽度不超过 `content-max`,左右至少留 `page-margin` + `space-3`;Composer 左右留 `page-margin`。
|
|
18
|
+
- **侧面板**:左边一条 1px `line`,`canvas` 底,默认占内容区的 40%,可以拖动。顶部是标签头,和顶栏同高、底部同样一条线;标签高 `panel-tab-height`,当前标签 `fill-selected` 底、`ink` 字,其他 `ink-secondary`;最右边是关闭、放大按钮。
|
|
19
|
+
- 窄于 `breakpoint-md`(960)时侧面板改成覆盖在内容上的抽屉;窄于 `breakpoint-sm`(760)时侧边栏也收起。
|
|
20
|
+
|
|
21
|
+
## Web
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
.dn-app { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; height: 100dvh; background: var(--canvas); color: var(--ink); }
|
|
25
|
+
.dn-app-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
26
|
+
.dn-app-content { flex: 1; min-height: 0; overflow: auto; padding: var(--space-6) calc(var(--page-margin) + var(--space-3)); }
|
|
27
|
+
|
|
28
|
+
.dn-toolbar {
|
|
29
|
+
position: sticky;
|
|
30
|
+
top: 0;
|
|
31
|
+
z-index: var(--z-sticky);
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: var(--space-2);
|
|
35
|
+
height: var(--toolbar-height);
|
|
36
|
+
padding: 0 var(--space-2) 0 var(--space-3);
|
|
37
|
+
border-bottom: var(--stroke-line) solid var(--line);
|
|
38
|
+
background: var(--canvas);
|
|
39
|
+
}
|
|
40
|
+
.dn-toolbar-title {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
gap: var(--space-1);
|
|
44
|
+
min-width: 0;
|
|
45
|
+
height: var(--button-height-md);
|
|
46
|
+
padding: 0 var(--space-2);
|
|
47
|
+
border: 0;
|
|
48
|
+
border-radius: var(--button-radius);
|
|
49
|
+
background: transparent;
|
|
50
|
+
color: var(--ink);
|
|
51
|
+
font: var(--weight-regular) var(--toolbar-title-size)/1 var(--font-sans);
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
.dn-toolbar-title span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
55
|
+
.dn-toolbar-title .dn-icon { width: var(--icon-size-sm); height: var(--icon-size-sm); color: var(--ink-secondary); }
|
|
56
|
+
.dn-toolbar-title:hover { background: var(--fill-hover); }
|
|
57
|
+
.dn-toolbar-actions { display: flex; align-items: center; gap: 2px; margin-left: auto; }
|
|
58
|
+
.dn-toolbar .dn-icon-button[aria-pressed="true"], .dn-panel-tabs .dn-icon-button[aria-pressed="true"] { background: var(--fill-selected); color: var(--ink); }
|
|
59
|
+
|
|
60
|
+
.dn-panel { display: flex; flex-direction: column; min-width: 0; min-height: 0; border-left: var(--stroke-line) solid var(--line); background: var(--canvas); }
|
|
61
|
+
.dn-panel-tabs {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 2px;
|
|
65
|
+
height: var(--toolbar-height);
|
|
66
|
+
padding: 0 var(--space-2);
|
|
67
|
+
border-bottom: var(--stroke-line) solid var(--line);
|
|
68
|
+
}
|
|
69
|
+
.dn-panel-tabs [role="tab"] {
|
|
70
|
+
height: var(--panel-tab-height);
|
|
71
|
+
padding: 0 var(--space-3);
|
|
72
|
+
border: 0;
|
|
73
|
+
border-radius: var(--button-radius);
|
|
74
|
+
background: transparent;
|
|
75
|
+
color: var(--ink-secondary);
|
|
76
|
+
font: var(--weight-regular) var(--type-callout-size)/1 var(--font-sans);
|
|
77
|
+
white-space: nowrap;
|
|
78
|
+
cursor: pointer;
|
|
79
|
+
}
|
|
80
|
+
.dn-panel-tabs [role="tab"]:hover { color: var(--ink); }
|
|
81
|
+
.dn-panel-tabs [role="tab"][aria-selected="true"] { background: var(--fill-selected); color: var(--ink); }
|
|
82
|
+
.dn-panel-tabs .dn-panel-actions { display: flex; gap: 2px; margin-left: auto; }
|
|
83
|
+
.dn-panel-body { flex: 1; min-height: 0; overflow: auto; padding: var(--space-3) var(--space-4); font: var(--type-code); color: var(--ink-secondary); }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```html
|
|
87
|
+
<div class="dn-app dn-app-demo">
|
|
88
|
+
<div class="dn-app-main">
|
|
89
|
+
<header class="dn-toolbar">
|
|
90
|
+
<button class="dn-toolbar-title" type="button"><span>Deernova UI 风格指南优化</span><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg></button>
|
|
91
|
+
<div class="dn-toolbar-actions">
|
|
92
|
+
<button class="dn-icon-button" type="button" aria-label="任务" aria-pressed="false"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 9h8M8 13h8M8 17h5"/></svg></button>
|
|
93
|
+
<button class="dn-icon-button" type="button" aria-label="终端" aria-pressed="true"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M5 7l5 5-5 5M12 17h7"/></svg></button>
|
|
94
|
+
<button class="dn-icon-button" type="button" aria-label="改动" aria-pressed="false"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M12 8v6M9 11h6M9 17h6"/></svg></button>
|
|
95
|
+
<button class="dn-icon-button" type="button" aria-label="更多"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="6" cy="12" r="1"/><circle cx="12" cy="12" r="1"/><circle cx="18" cy="12" r="1"/></svg></button>
|
|
96
|
+
</div>
|
|
97
|
+
</header>
|
|
98
|
+
<div class="dn-app-content"><div class="dn-message-assistant">内容区:对话、卡片和任务清单在这里滚动,顶栏保持不动。</div></div>
|
|
99
|
+
</div>
|
|
100
|
+
<aside class="dn-panel" aria-label="侧面板">
|
|
101
|
+
<div class="dn-panel-tabs" role="tablist">
|
|
102
|
+
<button type="button" role="tab" aria-selected="true">终端</button>
|
|
103
|
+
<button type="button" role="tab" aria-selected="false">预览</button>
|
|
104
|
+
<div class="dn-panel-actions">
|
|
105
|
+
<button class="dn-icon-button" type="button" aria-label="新建标签"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg></button>
|
|
106
|
+
<button class="dn-icon-button" type="button" aria-label="关闭面板"><svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="dn-panel-body">$ npm run dev<br>ready in 412 ms</div>
|
|
110
|
+
</aside>
|
|
111
|
+
</div>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## SwiftUI
|
|
115
|
+
|
|
116
|
+
macOS 和 iPadOS 用系统结构,顶栏和侧面板都是系统玻璃:
|
|
117
|
+
|
|
118
|
+
```swift
|
|
119
|
+
NavigationSplitView {
|
|
120
|
+
SidebarView()
|
|
121
|
+
} detail: {
|
|
122
|
+
ThreadView()
|
|
123
|
+
.navigationTitle(chat.title)
|
|
124
|
+
.toolbarTitleMenu { RenameButton(); Button("复制链接") { … } }
|
|
125
|
+
.toolbar {
|
|
126
|
+
ToolbarItemGroup {
|
|
127
|
+
Toggle("终端", systemImage: "terminal", isOn: $showsTerminal)
|
|
128
|
+
Button("更多", systemImage: "ellipsis") { … }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
.inspector(isPresented: $showsTerminal) { TerminalPanel() } // 侧面板
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## 不要
|
|
136
|
+
|
|
137
|
+
- 在 Web 应用的顶栏和侧面板上用玻璃或阴影(用不透明底加 1px 线)。
|
|
138
|
+
- 在 SwiftUI 端自己画工具栏、给系统工具栏加实色背景。
|
|
139
|
+
- 顶栏标题用粗体或衬线;工具按钮超过 7 个(收进「更多」)。
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# 无障碍
|
|
2
|
+
|
|
3
|
+
## 对比度
|
|
4
|
+
|
|
5
|
+
- 每个文字 token 在它说明里注明的纸面上不低于 4.5:1,两套主题都验算过(`npm test` 会检查)。
|
|
6
|
+
- 有意义的控件边界(输入框边框)不低于 3:1。
|
|
7
|
+
- 不要因为用了 token 就假定一定达标,要看它放在哪种纸面上。
|
|
8
|
+
|
|
9
|
+
## 点击区域
|
|
10
|
+
|
|
11
|
+
可见尺寸和点击区域是两回事:可见尺寸按组件的高度 token,点击区域不小于 `hit-min`(指针端 24,触控端 44)。差的部分用透明的命中区补足,**不加大可见的控件**。
|
|
12
|
+
|
|
13
|
+
- **Web**:给控件 `position: relative`,用 `::before` 伪元素把命中区撑到 `max(100%, var(--hit-min))`。不要在有背景的元素上加 padding:CSS 的 padding 在背景里面,会把可见的按钮一起撑大。写法见 [按钮](../components/button.md)。
|
|
14
|
+
- **SwiftUI**:在 `.buttonStyle()` 之后加 `.padding()`(这时 padding 在背景外面,是透明的),再加 `.contentShape(.rect)`。
|
|
15
|
+
- 相邻的两个控件,命中区不要重叠。
|
|
16
|
+
|
|
17
|
+
## 字号与缩放
|
|
18
|
+
|
|
19
|
+
- iOS 全面支持动态字体,在最大字号下也不截断文字:允许换行,横排放不下就改成竖排(`ViewThatFits`)。
|
|
20
|
+
- Web 在浏览器缩放到 200% 时,长中文、长英文和空数据都不溢出、不重叠。
|
|
21
|
+
|
|
22
|
+
## 其他
|
|
23
|
+
|
|
24
|
+
- 只有图标的按钮必须有无障碍标签。
|
|
25
|
+
- 只靠颜色传达的信息,一律补上图标或文字。
|
|
26
|
+
- 键盘焦点环清楚可见(`focus-ring`,只在 `:focus-visible` 时显示);Tab 顺序合理,Esc 能关闭弹层。
|
|
27
|
+
- 开启「减弱动态效果」时只保留淡入淡出;开启「降低透明度」时玻璃换成实色纸面。
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 颜色
|
|
2
|
+
|
|
3
|
+
只用黑、白、灰,强调靠明暗对比,不靠彩色。所有颜色都是语义 token,分浅色和深色两套主题,两套都验算过对比度。数值见 [设计变量](../tokens.md) 的「颜色」。
|
|
4
|
+
|
|
5
|
+
## 三层
|
|
6
|
+
|
|
7
|
+
1. **原始色板**(`gray-96`、`teal-27`……):名字里的数字是 HSL 亮度。只在 `tokens.json` 里被引用,代码里不要直接用。
|
|
8
|
+
2. **语义颜色**(`canvas`、`ink`、`line`……):代码里只用这一层。同一个名字在浅色和深色主题下取不同的值。
|
|
9
|
+
3. **组件**:组件说明里写的是语义颜色的组合,例如主按钮 = `accent` 底 + `on-accent` 字。
|
|
10
|
+
|
|
11
|
+
灰阶全部是中性灰,不偏暖也不偏冷。
|
|
12
|
+
|
|
13
|
+
## 纸面
|
|
14
|
+
|
|
15
|
+
从下到上:窗口底色和 Web 应用的内容区 `canvas` → 侧边栏和卡片 `surface` → 输入框、Composer、用户消息气泡、弹出层 `surface-raised`。代码块和展开的详情用 `surface-sunken`,表示“凹下去”。
|
|
16
|
+
|
|
17
|
+
- 侧边栏比内容区亮一层(`surface` 在 `canvas` 上),两者之间一条 `stroke-line`。
|
|
18
|
+
- 用户消息气泡比内容区亮一层(`surface-raised`),AI 回答直接排在内容区上,不加底色。
|
|
19
|
+
- 相邻的两层纸面之间靠亮度差和 1px 线区分,不靠阴影。
|
|
20
|
+
|
|
21
|
+
## 文字
|
|
22
|
+
|
|
23
|
+
- 正文和标题用 `ink`,次要信息用 `ink-secondary`,占位符、时间戳、计数用 `ink-tertiary`。三者在四种纸面上都不低于 4.5:1。
|
|
24
|
+
- 链接用 `ink` 加下划线(`line-control` 色、1px、下偏移 3px),不用彩色。
|
|
25
|
+
- 不要用透明度调文字的深浅,直接选对应的文字 token。
|
|
26
|
+
|
|
27
|
+
## 线条
|
|
28
|
+
|
|
29
|
+
分隔线和卡片描边用 `line`,属于装饰。输入框边框用 `line-control`,不低于 3:1,让人一眼看出这里可以输入。
|
|
30
|
+
|
|
31
|
+
## 强调
|
|
32
|
+
|
|
33
|
+
- `accent` 等于 `ink`,用于主按钮填充、选中指示、焦点环。主按钮上的文字用 `on-accent`:深色主题下它是黑色,不要写死白色。
|
|
34
|
+
- 浅灰的强调底用 `accent-soft`,上面的文字用 `ink`。
|
|
35
|
+
- `focus-ring` 等于 `accent`,宽 `stroke-focus`,外偏移 `focus-offset`。
|
|
36
|
+
|
|
37
|
+
## 状态色
|
|
38
|
+
|
|
39
|
+
`success`(青绿)、`warning`(赭黄)、`danger`(莓红)、`info`(蓝),各自配一个 `-soft` 浅底。这是界面里仅有的彩色:
|
|
40
|
+
|
|
41
|
+
- 只用在状态上,不拿来装饰,不做品牌色。
|
|
42
|
+
- 状态必须同时带图标或文字,不能只靠颜色区分。
|
|
43
|
+
- `success` 特意偏青,和 `danger` 拉开距离,色弱用户也能分辨。
|
|
44
|
+
- 开关打开时用 `success`:黑白开关在深色主题下看不清圆钮。
|
|
45
|
+
|
|
46
|
+
## 交互底色
|
|
47
|
+
|
|
48
|
+
悬停用 `fill-hover`,选中用 `fill-selected`。它们是半透明的黑或白,叠在任何纸面上都成立,所以列表行、侧边栏项、次按钮、文字按钮都用它们,而不是另选一个灰色。选中要一眼看得出来:`fill-selected` 比悬停明显得多。
|
|
49
|
+
|
|
50
|
+
## 仅网页
|
|
51
|
+
|
|
52
|
+
`glass-fill`、`glass-stroke`、`shadow-glass` 只用于官网与文档的页眉。Web 应用不用玻璃;App 里一律用系统玻璃,不要用这些值自己画。
|
|
53
|
+
|
|
54
|
+
## 不要
|
|
55
|
+
|
|
56
|
+
- 加品牌色、渐变、彩色图标。
|
|
57
|
+
- 写死十六进制颜色;用 `opacity` 把 `ink` 调淡当次要文字。
|
|
58
|
+
- 只用颜色区分状态。
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 层级与阴影
|
|
2
|
+
|
|
3
|
+
阴影就是层级:只有真正浮起来的东西才有阴影。数值见 [设计变量](../tokens.md) 的「阴影」和「层级」。
|
|
4
|
+
|
|
5
|
+
| 层 | 是什么 | 阴影 | 叠放顺序 |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| 0 纸面 | `canvas`、`surface`,以及用 1px 线分隔的侧边栏、顶栏、内容块、卡片 | 无 | 默认 |
|
|
8
|
+
| 1 控件厚度 | 分段控件的选中块、开关圆钮 | `shadow-control` | 默认 |
|
|
9
|
+
| 2 浮层 | Composer、弹出菜单、弹窗、浮动面板 | `shadow-float` | `z-dropdown`、`z-overlay` |
|
|
10
|
+
| 玻璃 | SwiftUI 端的工具栏、标签栏、浮动按钮;官网页眉 | 系统玻璃自带;官网页眉用 `shadow-glass` | `z-sticky` |
|
|
11
|
+
| 提示 | 轻提示 | 跟随玻璃 | `z-toast` |
|
|
12
|
+
|
|
13
|
+
- 卡片不加阴影。卡片和纸面的区分靠 `line` 线和亮度差。
|
|
14
|
+
- Web 应用的顶栏是不透明的纸面,吸顶时用 `z-sticky`,底部一条 `stroke-line`,不加阴影。
|
|
15
|
+
- 弹窗背后用 `scrim` 压暗内容。
|
|
16
|
+
- z-index 只用这几档,不要写 9999 这类数字。SwiftUI 里叠放交给系统的 sheet、popover、overlay。
|
|
17
|
+
- 深色主题下阴影更重(不透明度更高),因为深色纸面上浅阴影看不见;这已经写在两套主题的值里。
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# 玻璃
|
|
2
|
+
|
|
3
|
+
内容放在不透明的纸面上,操作层按平台处理:原生端交给系统的 Liquid Glass,Web 应用用不透明平面加 1px 线。
|
|
4
|
+
|
|
5
|
+
## 为什么按平台分
|
|
6
|
+
|
|
7
|
+
- **SwiftUI 端(macOS、iOS、iPadOS)**:iOS 26、iPadOS 26、macOS 26 的系统导航结构自带 Liquid Glass,不写代码就有,还自带滚动边缘效果和「降低透明度」处理。这是原生端的优势,直接用;盖掉它反而要额外写背景,也不像新系统上的应用。
|
|
8
|
+
- **Web 应用**:浏览器没有系统玻璃,用模糊效果模仿既费性能又显得廉价。操作层用不透明平面(顶栏 `canvas`、侧边栏 `surface`)加 `stroke-line` 分隔线,骨架和 Claude 桌面端一致。
|
|
9
|
+
- **官网与文档**:吸顶页眉可以用轻玻璃(`glass-fill`、`glass-blur`),内容从它下面滚过去。这是展示页面的点缀,不用在应用里。
|
|
10
|
+
|
|
11
|
+
## SwiftUI 端:用玻璃的地方
|
|
12
|
+
|
|
13
|
+
大部分由系统自动提供:
|
|
14
|
+
|
|
15
|
+
- 导航栏、工具栏、标签栏、侧边栏:使用系统的标准导航结构,系统会自动渲染成玻璃。
|
|
16
|
+
- 浮动操作按钮、浮动工具条。相邻的几个玻璃元素放在一组,让它们互相融合。
|
|
17
|
+
- 底部弹窗、弹出菜单、popover:系统自动处理。
|
|
18
|
+
- 玻璃层上的主操作用实色玻璃(`accent` 底、`on-accent` 图标),次要操作用普通玻璃。
|
|
19
|
+
|
|
20
|
+
## 不用玻璃的地方
|
|
21
|
+
|
|
22
|
+
卡片、列表行、消息、输入区、Composer、代码块、任务清单,以及所有需要长时间阅读的内容。Web 应用的任何部分。
|
|
23
|
+
|
|
24
|
+
## 规则
|
|
25
|
+
|
|
26
|
+
- 玻璃上不叠玻璃。
|
|
27
|
+
- 玻璃上只放图标和很短的标签,不放段落和列表。
|
|
28
|
+
- 只有主操作的玻璃才用实色,一屏最多一个。
|
|
29
|
+
- 让内容从工具栏下面滚过去,保留系统的滚动边缘效果。不要给系统工具栏、侧边栏加实色背景。
|
|
30
|
+
- 用户开启「降低透明度」后,玻璃要换成不透明的 `surface-raised`。系统玻璃会自动处理,自己写的半透明效果也要做同样的处理。
|
|
31
|
+
|
|
32
|
+
## 写法
|
|
33
|
+
|
|
34
|
+
- **SwiftUI**:`.buttonStyle(.glass)`、`.glassProminent`、`GlassEffectContainer`、`.glassEffect()`,见 [SwiftUI 共用](../scenes/swiftui.md) 和 [玻璃操作层](../components/glass-controls.md)。
|
|
35
|
+
- **Web 应用**:不用玻璃,顶栏和侧边栏见 [顶栏与侧面板](../components/toolbar.md)、[侧边栏](../components/sidebar.md)。
|
|
36
|
+
- **官网页眉**:见 [官网与文档](../scenes/web-marketing.md)。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# 图标
|
|
2
|
+
|
|
3
|
+
图标是灰色的线性符号,粗细跟随旁边的文字。
|
|
4
|
+
|
|
5
|
+
## 尺寸
|
|
6
|
+
|
|
7
|
+
<!-- tokens:icon -->
|
|
8
|
+
| Token | 桌面<br>Web 应用 · macOS 内容区 | 触控<br>iOS · iPadOS · Web 触屏 | 舒展<br>官网与文档 | 用途 |
|
|
9
|
+
| --- | --- | --- | --- | --- |
|
|
10
|
+
| `icon-size-sm` | 14 | 17 | 16 | 按钮和标签里的图标。 |
|
|
11
|
+
| `icon-size-md` | 16 | 20 | 18 | 侧边栏、列表、横幅、工具栏里的图标。 |
|
|
12
|
+
| `icon-size-lg` | 20 | 24 | 24 | 玻璃按钮、标签栏里的图标。 |
|
|
13
|
+
|
|
14
|
+
| Token | 值 | 用途 |
|
|
15
|
+
| --- | --- | --- |
|
|
16
|
+
| `icon-stroke` | `1.75px` | 线性图标在 24×24 viewBox 里的描边宽度,接近 SF Symbols 的常规粗细。(仅网页) |
|
|
17
|
+
<!-- /tokens -->
|
|
18
|
+
|
|
19
|
+
## 规则
|
|
20
|
+
|
|
21
|
+
- App 里只用 SF Symbols,和系统保持一致。13pt 文字旁的图标用 `.medium`,看起来更清楚。
|
|
22
|
+
- Web 上用线条粗细接近 SF Symbols 的线性图标:24×24 viewBox、`icon-stroke` 描边、圆角端点和拐角(`line-cap`、`line-join`)、无填充。不用 emoji 当图标。
|
|
23
|
+
- 默认颜色 `ink-secondary`,选中态和主操作用 `ink`。图标不用彩色,状态图标除外。
|
|
24
|
+
- 状态图标固定为:成功 `checkmark.circle.fill`,警告 `exclamationmark.triangle.fill`,错误 `xmark.octagon.fill`,提示 `info.circle.fill`。Web 上用同样形状的线性图标。
|
|
25
|
+
- 只有图标的按钮必须有无障碍标签(`aria-label`,或 SwiftUI 的 `Button("搜索", systemImage:)` 加 `.labelStyle(.iconOnly)`)。
|
|
26
|
+
|
|
27
|
+
## Web 写法
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
.dn-icon {
|
|
31
|
+
width: var(--icon-size-md);
|
|
32
|
+
height: var(--icon-size-md);
|
|
33
|
+
flex: none;
|
|
34
|
+
fill: none;
|
|
35
|
+
stroke: currentColor;
|
|
36
|
+
stroke-width: var(--icon-stroke);
|
|
37
|
+
stroke-linecap: var(--line-cap);
|
|
38
|
+
stroke-linejoin: var(--line-join);
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<svg class="dn-icon" viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="M20 20l-4.5-4.5"/></svg>
|
|
44
|
+
```
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 动效
|
|
2
|
+
|
|
3
|
+
动效用来说明因果:东西从哪里来、到哪里去。数值见 [设计变量](../tokens.md) 的「动效」。
|
|
4
|
+
|
|
5
|
+
| Token | 值 | 用途 |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `duration-instant` | 120ms | 悬停、按压、选中这类即时反馈 |
|
|
8
|
+
| `duration-base` | 200ms | 展开、收起、切换、开关 |
|
|
9
|
+
| `duration-slow` | 320ms | 面板滑入、页面级转场 |
|
|
10
|
+
| `ease-standard` | cubic-bezier(0.2, 0.8, 0.2, 1) | 默认缓动:快进、缓停 |
|
|
11
|
+
|
|
12
|
+
- SwiftUI 默认用 `.spring(response: 0.35, dampingFraction: 0.85)`,手感干脆、不回弹过头;即时反馈用 `.easeOut(duration: Deernova.durationInstant)`。弹窗、导航的转场交给系统。
|
|
13
|
+
- Web 上 `transition: background-color var(--duration-instant) var(--ease-standard)`。只过渡颜色、不透明度和 transform,不过渡 width、height。
|
|
14
|
+
|
|
15
|
+
## 规则
|
|
16
|
+
|
|
17
|
+
- 业务界面里不做无目的的循环动画。品牌展示页可以有叙事动效,但要能暂停,也不能妨碍阅读。
|
|
18
|
+
- 用户开启「减弱动态效果」后,位移和缩放换成淡入淡出,完整信息不能依赖动画才看得到。
|
|
19
|
+
- **布局保持稳定**:给会变化的标题、状态和按钮文字预留空间,切换时阅读位置和按钮位置不跳动。禁用的按钮保持原位,不隐藏。加载时按钮宽度不变,把文字换成转圈。
|
|
20
|
+
- **状态不丢失**:重绘、切换主题、窗口缩放时,焦点、选中项和用户输入都要保留。
|
|
21
|
+
- **中断可恢复**:装饰性动画在暂停、离开视野或页面隐藏时冻结,回来后从原处继续,不追赶错过的时间;和真实时间绑定的功能(倒计时等)仍以真实时间为准。
|
|
22
|
+
- 连续的装饰动画在 Web 上用 `requestAnimationFrame` 按时间推进,`visibilitychange` 时暂停。
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 圆角
|
|
2
|
+
|
|
3
|
+
圆角刻度是固定值;每个组件用哪一档,由组件的 `*-radius` token 按密度决定。数值见 [设计变量](../tokens.md) 的「圆角」和「组件尺寸」。
|
|
4
|
+
|
|
5
|
+
## 刻度
|
|
6
|
+
|
|
7
|
+
| Token | 值 | 典型用途 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `radius-xs` | 4 | 标签、复选框、小按钮 |
|
|
10
|
+
| `radius-sm` | 6 | 桌面端的按钮、输入框、工具栏图标按钮 |
|
|
11
|
+
| `radius-md` | 8 | 侧边栏选中块、代码块、弹出菜单 |
|
|
12
|
+
| `radius-lg` | 12 | 卡片、用户消息气泡、分组列表、任务清单 |
|
|
13
|
+
| `radius-xl` | 24 | 底部弹窗、触控端单行 Composer |
|
|
14
|
+
| `radius-full` | 999 | 胶囊和圆形:触控端按钮、玻璃按钮、状态圆点 |
|
|
15
|
+
|
|
16
|
+
## 按场景
|
|
17
|
+
|
|
18
|
+
- 桌面端(Web 应用、macOS 内容区):按钮和输入框 6,侧边栏选中块 8,提示条 10,卡片、气泡、任务清单 12,Composer 14。小的东西圆角小,大的容器圆角大。
|
|
19
|
+
- 触控端(iOS、iPadOS、Web 触屏):按钮是胶囊,卡片和分组列表 12,底部弹窗 24。
|
|
20
|
+
- 官网:按钮是胶囊,卡片 12。
|
|
21
|
+
|
|
22
|
+
这些差异已经写进 `button-radius`、`field-radius`、`row-radius`、`banner-radius`、`card-radius`、`composer-radius`、`sheet-radius` 等组件 token,直接用它们,不要在代码里按平台判断。
|
|
23
|
+
|
|
24
|
+
## 同心
|
|
25
|
+
|
|
26
|
+
嵌套时保持同心:内圆角 = 外圆角 − 内边距。例如分段控件的底槽 `segmented-radius` 是 6、内边距 2,选中块就是 4。SwiftUI 里用 `ConcentricRectangle()`,内层会自动和外层同心。
|
|
27
|
+
|
|
28
|
+
## 不要
|
|
29
|
+
|
|
30
|
+
- 在一个界面里混用多种无关的圆角。
|
|
31
|
+
- 嵌套的圆角内外一样大(内层看起来会“胖”出来)。
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# 间距与布局
|
|
2
|
+
|
|
3
|
+
4pt 网格,整体偏紧凑。层次靠间距建立,不靠加更多的线和框。数值见 [设计变量](../tokens.md) 的「间距」和「布局」。
|
|
4
|
+
|
|
5
|
+
## 刻度和语义间距
|
|
6
|
+
|
|
7
|
+
刻度 `space-1`(4)到 `space-16`(64)是固定值,日常布局基本只用 `space-1` 到 `space-4`。
|
|
8
|
+
|
|
9
|
+
更常用的是按密度取值的语义间距,写代码时优先用它们。内容区里段落、工具调用行、卡片之间统一 `space-4`(16)。
|
|
10
|
+
|
|
11
|
+
| Token | 用在哪里 |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `gap-inline` | 同一行里图标和文字之间、按钮里图标和标签之间 |
|
|
14
|
+
| `gap-stack` | 同一组里相邻元素之间:表单项、卡片里的段落、并排的按钮 |
|
|
15
|
+
| `gap-group` | 组与组之间:设置分组、页面区块。至少是 `gap-stack` 的 2 倍 |
|
|
16
|
+
| `page-margin` | 页面和内容区的左右边距 |
|
|
17
|
+
|
|
18
|
+
组件内部的间距(卡片内边距、按钮左右内边距等)见各组件的尺寸表。
|
|
19
|
+
|
|
20
|
+
## 规则
|
|
21
|
+
|
|
22
|
+
- 组与组之间的间距至少是组内间距的 2 倍。
|
|
23
|
+
- 控件的高度由高度 token 决定(`button-height-*`、`field-height`、`row-height`),不用上下内边距去撑。上下内边距只用于多行内容:卡片、横幅、Composer。
|
|
24
|
+
- 同一行里的控件用同一个高度档位。
|
|
25
|
+
- 用 1px 线和少量留白分组;不要用大块空白,也不要每组都套一个框。
|
|
26
|
+
|
|
27
|
+
## 布局
|
|
28
|
+
|
|
29
|
+
- 对话栏和长文阅读栏的宽度不超过 `content-max`(680)。
|
|
30
|
+
- 侧边栏默认宽 `sidebar-width`(桌面 288),用户可以拖动调整。iPhone 没有侧边栏。
|
|
31
|
+
- Web 应用的顶栏高 `toolbar-height`(44),侧面板的标签头和它对齐。
|
|
32
|
+
- 对话栏:内容左右各留 44,Composer 左右各留 32,宽度不超过 `content-max`。
|
|
33
|
+
- 断点:`breakpoint-sm`(760)以下侧边栏收起、多栏变单栏;`breakpoint-md`(960)以下收起目录这类次要栏;`breakpoint-lg`(1280)以上内容不再变宽。断点只能写在媒体查询里,所以没有对应的 CSS 变量。
|
|
34
|
+
- 窄屏下重新排列,不能出现横向滚动。
|
|
35
|
+
|
|
36
|
+
## 触控端比指针端大是有意的
|
|
37
|
+
|
|
38
|
+
手指比鼠标指针粗,触控端的控件和行高都更大(默认按钮 36 对比桌面的 28)。在 Mac 屏幕上看 iOS 设计图会比真机上大 20%–40%,判断大小时以真机为准。
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# 线条
|
|
2
|
+
|
|
3
|
+
用线条代替阴影来分组。这里包括线宽(多粗)和线形(实线、虚线、端点)。数值见 [设计变量](../tokens.md) 的「线宽」和「线形」。
|
|
4
|
+
|
|
5
|
+
## 线宽
|
|
6
|
+
|
|
7
|
+
| Token | 值 | 用途 |
|
|
8
|
+
| --- | --- | --- |
|
|
9
|
+
| `stroke-line` | 1px | 分隔线、卡片、输入区、侧边栏和顶栏的边线 |
|
|
10
|
+
| `stroke-control` | 1px | 输入框边框、选中卡片的描边 |
|
|
11
|
+
| `stroke-focus` | 2px | 键盘焦点环 |
|
|
12
|
+
| `focus-offset` | 2px | 焦点环和控件边缘之间的距离 |
|
|
13
|
+
| `icon-stroke` | 1.75 | 线性图标在 24×24 viewBox 里的描边(仅网页) |
|
|
14
|
+
|
|
15
|
+
- 线统一是 1px(高分屏上 2 个物理像素),清楚、不发虚。
|
|
16
|
+
- 有意义的控件边界(输入框)用 `stroke-control` 加 `line-control` 色,保证 3:1;装饰性的线用 `stroke-line` 加 `line` 色。
|
|
17
|
+
- SwiftUI 里自己画的边框(卡片、Composer、任务清单)用 1pt;系统自带的分隔线(`Divider()`、`List` 的行分隔)保持系统样式,不去改。
|
|
18
|
+
|
|
19
|
+
## 线形
|
|
20
|
+
|
|
21
|
+
| Token | 值 | 用途 |
|
|
22
|
+
| --- | --- | --- |
|
|
23
|
+
| `line-style-divider` | solid | 分隔线、卡片和输入框的边框。界面里的线默认都是实线 |
|
|
24
|
+
| `line-style-dropzone` | dashed | 拖放区、待添加的占位块 |
|
|
25
|
+
| `line-dash` | 4 4 | SVG 虚线的 `stroke-dasharray`(仅网页) |
|
|
26
|
+
| `line-cap` / `line-join` | round | 图标和自绘线条的端点和拐角 |
|
|
27
|
+
|
|
28
|
+
- 只有“还空着、等你放东西进来”的区域用虚线。不要用虚线做装饰或分隔。
|
|
29
|
+
- 图标、图表里自绘的线一律圆角端点、圆角拐角。
|
|
30
|
+
|
|
31
|
+
## 规则
|
|
32
|
+
|
|
33
|
+
- 侧边栏和内容区之间、顶栏和内容之间各一条线;列表分隔线从文字起点开始,不贯穿到左边缘。
|
|
34
|
+
- 区块之间一条线就够,不要用双线或粗线强调。
|
|
35
|
+
- 卡片用线描边,不加阴影;悬停时描边加深为 `line-control`,选中时变成 `ink`。
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# 字体
|
|
2
|
+
|
|
3
|
+
全部使用系统字体,不打包字体文件。无衬线做界面,衬线只用来点题。
|
|
4
|
+
|
|
5
|
+
| 角色 | 西文 | 中文 | 用在哪里 |
|
|
6
|
+
| --- | --- | --- | --- |
|
|
7
|
+
| 无衬线 `--font-sans` | SF Pro | 苹方 | 所有界面文字:按钮、标签、列表、表单、标题栏、AI 回答、用户消息 |
|
|
8
|
+
| 等宽 `--font-mono` | SF Mono | 苹方 | 代码、命令、文件路径、需要对齐的数据 |
|
|
9
|
+
| 衬线 `--font-serif` | New York | 苹方 | 只用在页面大标题、空状态标题、官网与文档、文章类长文 |
|
|
10
|
+
|
|
11
|
+
Windows 和 Android 上会落到各自的系统字体,这是预期的,不要为此加载网络字体。
|
|
12
|
+
|
|
13
|
+
## 字号角色
|
|
14
|
+
|
|
15
|
+
字号按角色组织,不按平台。每个角色在三种密度下各有一组字号和行高,完整数值见 [设计变量](../tokens.md) 的「字号角色」。
|
|
16
|
+
|
|
17
|
+
| 角色 | 字族 | 用途 |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `display` | 衬线 600 | 空状态、引导页、页面的大标题,每屏最多一个 |
|
|
20
|
+
| `title` | 衬线 600 | 文章标题、弹窗标题 |
|
|
21
|
+
| `headline` | 无衬线 600 | 区块标题、卡片标题、横幅标题、列表行标题 |
|
|
22
|
+
| `body` | 无衬线 400 | 界面正文、AI 回答、用户消息、列表、表单。桌面端 15/24 |
|
|
23
|
+
| `prose` | 衬线 400 | 文章、文档这类长文阅读。AI 回答不用它 |
|
|
24
|
+
| `callout` | 无衬线 400 | 说明文字、弹窗正文、输入区下方的模型和权限信息 |
|
|
25
|
+
| `footnote` | 无衬线 400 | 侧边栏分组标题、分组列表的页头页脚、卡片摘要 |
|
|
26
|
+
| `caption` | 无衬线 400 | 时间戳、计数、标签 |
|
|
27
|
+
| `code` | 等宽 400 | 代码块、命令、文件路径 |
|
|
28
|
+
|
|
29
|
+
- Web:`font: var(--type-body)` 一次写全字重、字号、行高和字族;只要字号时用 `--type-body-size`。
|
|
30
|
+
- SwiftUI:`.font(.deernovaBody)`。macOS 上是固定字号(`.system(size: 15)`),只用在自己画的内容区;系统控件保持系统字号。iOS 上是文本样式(`.body`、`.headline`),动态字体会生效,不要写死 `size:`。
|
|
31
|
+
- 按钮、输入框、标签有自己的字号 token(`button-font-size-*`、`field-font-size`、`tag-font-size`),见组件说明。
|
|
32
|
+
|
|
33
|
+
## 字重
|
|
34
|
+
|
|
35
|
+
只用 400、500、600(`weight-regular`、`weight-medium`、`weight-semibold`),不用 700 以上。层次先靠字号和灰度,再考虑字重:
|
|
36
|
+
|
|
37
|
+
- 600:`display`、`title`、`headline`,以及 AI 回答里的加粗。
|
|
38
|
+
- 500:按钮、标签、表单标签。侧边栏的选中项靠底色区分,不加粗。
|
|
39
|
+
- 400:其余全部,包括侧边栏分组标题(`footnote` + `ink-tertiary`)、会话名、顶栏标题、说明、时间戳。
|
|
40
|
+
|
|
41
|
+
## 规则
|
|
42
|
+
|
|
43
|
+
- 界面文字行高约为字号的 1.4–1.6 倍;正文 15/24,中文读起来不挤。
|
|
44
|
+
- 只有 `display` 收紧字距(`--type-display-tracking`),其余保持默认。
|
|
45
|
+
- 需要上下对齐的数字(计数、价格、时间)用等宽数字:`font-variant-numeric: tabular-nums` 或 `.monospacedDigit()`。
|
|
46
|
+
- 想让中文标题也有衬线的气质,可以打包思源宋体(Source Han Serif,开源可商用)的子集,只用在官网大标题上。
|
|
47
|
+
- 界面紧凑不等于字小:最小的字是 12(`caption`)。
|
|
48
|
+
|
|
49
|
+
## 不要
|
|
50
|
+
|
|
51
|
+
- 给按钮、标签、表单、AI 回答用衬线。
|
|
52
|
+
- 用粗体做分组标题,或在一屏里堆很多粗体。
|
|
53
|
+
- 让按钮、输入框继承页面的行高。控件里的文字行高设为 1,高度交给 `height`。
|
|
54
|
+
- 在 iOS 上写死字号让动态字体失效。
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# 文案
|
|
2
|
+
|
|
3
|
+
- 用“你”称呼用户。语气平实直接,不用感叹号,不用 emoji。
|
|
4
|
+
- 按钮以动词开头,说清点了会发生什么:「保存」「新建笔记」「删除 3 个文件」。不用「确定」「OK」。操作完成后的提示用完成态:「已保存」「已复制」。
|
|
5
|
+
- 错误提示要说明出了什么问题、怎么解决:「网络连接已断开,恢复后会自动重试。」不道歉,不含糊。
|
|
6
|
+
- 中文与英文、中文与数字之间加一个半角空格:「同步到 iCloud」「剩余 12 次」。数字和单位用半角。
|
|
7
|
+
- 标题、按钮、标签不加句号;完整的句子加句号。
|
|
8
|
+
- 空状态写法:标题说明现在的状态,正文告诉用户下一步做什么。例如「还没有笔记」/「写下第一条想法,它会同步到你所有的设备。」
|
|
9
|
+
- 页脚和说明写清楚后果:「关闭 iCloud 同步后,新笔记只保存在这台设备上。」
|
|
10
|
+
- 品牌名写作 Deernova。网站页眉、页脚这类品牌位置使用小写衬线的文字标识 `deernova.`(句点属于标识);App 界面里写 Deernova 或具体的应用名。目前没有图形标志。
|