@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.
Files changed (133) hide show
  1. package/README.md +19 -9
  2. package/bin/cli.mjs +212 -32
  3. package/package.json +1 -1
  4. package/skill/SKILL.md +90 -22
  5. package/skill/assets/DeernovaTokens.swift +376 -0
  6. package/skill/assets/screens/iphone-button-sizes-dark.png +0 -0
  7. package/skill/assets/screens/iphone-button-sizes-light.png +0 -0
  8. package/skill/assets/screens/iphone-screens-dark.png +0 -0
  9. package/skill/assets/screens/iphone-screens-light.png +0 -0
  10. package/skill/assets/screens/mac-window-dark.png +0 -0
  11. package/skill/assets/screens/mac-window-light.png +0 -0
  12. package/skill/assets/tailwind.css +106 -0
  13. package/skill/assets/tokens.css +533 -0
  14. package/skill/assets/tokens.json +1686 -0
  15. package/skill/manifest.json +2 -22
  16. package/skill/references/components/banner.md +95 -0
  17. package/skill/references/components/button.md +181 -0
  18. package/skill/references/components/card.md +89 -0
  19. package/skill/references/components/code-block.md +77 -0
  20. package/skill/references/components/composer.md +206 -0
  21. package/skill/references/components/empty-state.md +63 -0
  22. package/skill/references/components/glass-controls.md +127 -0
  23. package/skill/references/components/list.md +130 -0
  24. package/skill/references/components/message.md +94 -0
  25. package/skill/references/components/segmented.md +79 -0
  26. package/skill/references/components/sheet.md +84 -0
  27. package/skill/references/components/sidebar.md +139 -0
  28. package/skill/references/components/step-row.md +135 -0
  29. package/skill/references/components/tag.md +86 -0
  30. package/skill/references/components/task-list.md +83 -0
  31. package/skill/references/components/text-field.md +86 -0
  32. package/skill/references/components/toolbar.md +139 -0
  33. package/skill/references/foundations/accessibility.md +27 -0
  34. package/skill/references/foundations/color.md +58 -0
  35. package/skill/references/foundations/elevation.md +17 -0
  36. package/skill/references/foundations/glass.md +36 -0
  37. package/skill/references/foundations/iconography.md +44 -0
  38. package/skill/references/foundations/motion.md +22 -0
  39. package/skill/references/foundations/radius.md +31 -0
  40. package/skill/references/foundations/spacing.md +38 -0
  41. package/skill/references/foundations/stroke.md +35 -0
  42. package/skill/references/foundations/typography.md +54 -0
  43. package/skill/references/foundations/writing.md +10 -0
  44. package/skill/references/review.md +26 -13
  45. package/skill/references/scenes/ios.md +61 -0
  46. package/skill/references/scenes/ipados.md +42 -0
  47. package/skill/references/scenes/macos.md +65 -0
  48. package/skill/references/scenes/swiftui.md +94 -0
  49. package/skill/references/scenes/web-marketing.md +34 -0
  50. package/skill/references/scenes/web.md +83 -0
  51. package/skill/references/tokens.md +441 -74
  52. package/skill/assets/design/README.md +0 -170
  53. package/skill/assets/design/components/Banner/README.md +0 -33
  54. package/skill/assets/design/components/Banner/preview.html +0 -26
  55. package/skill/assets/design/components/Button/README.md +0 -59
  56. package/skill/assets/design/components/Button/preview.html +0 -27
  57. package/skill/assets/design/components/Card/README.md +0 -31
  58. package/skill/assets/design/components/Card/preview.html +0 -24
  59. package/skill/assets/design/components/CodeBlock/README.md +0 -34
  60. package/skill/assets/design/components/CodeBlock/preview.html +0 -16
  61. package/skill/assets/design/components/Composer/README.md +0 -42
  62. package/skill/assets/design/components/Composer/preview.html +0 -28
  63. package/skill/assets/design/components/Cover/preview.html +0 -53
  64. package/skill/assets/design/components/EmptyState/README.md +0 -28
  65. package/skill/assets/design/components/EmptyState/preview.html +0 -16
  66. package/skill/assets/design/components/GlassControls/README.md +0 -43
  67. package/skill/assets/design/components/GlassControls/preview.html +0 -40
  68. package/skill/assets/design/components/ListGroup/README.md +0 -39
  69. package/skill/assets/design/components/ListGroup/preview.html +0 -22
  70. package/skill/assets/design/components/MacWindow/README.md +0 -27
  71. package/skill/assets/design/components/MacWindow/preview.html +0 -70
  72. package/skill/assets/design/components/Message/README.md +0 -31
  73. package/skill/assets/design/components/Message/preview.html +0 -21
  74. package/skill/assets/design/components/PhoneScreen/README.md +0 -25
  75. package/skill/assets/design/components/PhoneScreen/preview.html +0 -74
  76. package/skill/assets/design/components/Segmented/README.md +0 -21
  77. package/skill/assets/design/components/Segmented/preview.html +0 -21
  78. package/skill/assets/design/components/Sheet/README.md +0 -29
  79. package/skill/assets/design/components/Sheet/preview.html +0 -31
  80. package/skill/assets/design/components/Sidebar/README.md +0 -36
  81. package/skill/assets/design/components/Sidebar/preview.html +0 -20
  82. package/skill/assets/design/components/StepRow/README.md +0 -35
  83. package/skill/assets/design/components/StepRow/preview.html +0 -17
  84. package/skill/assets/design/components/Tag/README.md +0 -36
  85. package/skill/assets/design/components/Tag/preview.html +0 -22
  86. package/skill/assets/design/components/TextField/README.md +0 -29
  87. package/skill/assets/design/components/TextField/preview.html +0 -28
  88. package/skill/assets/design/components/bundle.css +0 -189
  89. package/skill/assets/design/design-system.json +0 -25
  90. package/skill/assets/design/images/Banner-dark.png +0 -0
  91. package/skill/assets/design/images/Banner-light.png +0 -0
  92. package/skill/assets/design/images/Button-dark.png +0 -0
  93. package/skill/assets/design/images/Button-light.png +0 -0
  94. package/skill/assets/design/images/Card-dark.png +0 -0
  95. package/skill/assets/design/images/Card-light.png +0 -0
  96. package/skill/assets/design/images/CodeBlock-dark.png +0 -0
  97. package/skill/assets/design/images/CodeBlock-light.png +0 -0
  98. package/skill/assets/design/images/Composer-dark.png +0 -0
  99. package/skill/assets/design/images/Composer-light.png +0 -0
  100. package/skill/assets/design/images/Cover-dark.png +0 -0
  101. package/skill/assets/design/images/Cover-light.png +0 -0
  102. package/skill/assets/design/images/EmptyState-dark.png +0 -0
  103. package/skill/assets/design/images/EmptyState-light.png +0 -0
  104. package/skill/assets/design/images/Example-iOSButtonsInPhone-dark.png +0 -0
  105. package/skill/assets/design/images/Example-iOSButtonsInPhone-light.png +0 -0
  106. package/skill/assets/design/images/GlassControls-dark.png +0 -0
  107. package/skill/assets/design/images/GlassControls-light.png +0 -0
  108. package/skill/assets/design/images/ListGroup-dark.png +0 -0
  109. package/skill/assets/design/images/ListGroup-light.png +0 -0
  110. package/skill/assets/design/images/MacWindow-dark.png +0 -0
  111. package/skill/assets/design/images/MacWindow-light.png +0 -0
  112. package/skill/assets/design/images/Message-dark.png +0 -0
  113. package/skill/assets/design/images/Message-light.png +0 -0
  114. package/skill/assets/design/images/PhoneScreen-dark.png +0 -0
  115. package/skill/assets/design/images/PhoneScreen-light.png +0 -0
  116. package/skill/assets/design/images/Segmented-dark.png +0 -0
  117. package/skill/assets/design/images/Segmented-light.png +0 -0
  118. package/skill/assets/design/images/Sheet-dark.png +0 -0
  119. package/skill/assets/design/images/Sheet-light.png +0 -0
  120. package/skill/assets/design/images/Sidebar-dark.png +0 -0
  121. package/skill/assets/design/images/Sidebar-light.png +0 -0
  122. package/skill/assets/design/images/StepRow-dark.png +0 -0
  123. package/skill/assets/design/images/StepRow-light.png +0 -0
  124. package/skill/assets/design/images/Tag-dark.png +0 -0
  125. package/skill/assets/design/images/Tag-light.png +0 -0
  126. package/skill/assets/design/images/TextField-dark.png +0 -0
  127. package/skill/assets/design/images/TextField-light.png +0 -0
  128. package/skill/assets/design/tokens.css +0 -187
  129. package/skill/assets/design/tokens.json +0 -142
  130. package/skill/references/components.md +0 -648
  131. package/skill/references/foundations.md +0 -170
  132. package/skill/references/native.md +0 -7
  133. package/skill/references/web.md +0 -11
@@ -1,13 +1,26 @@
1
- # Review criteria
2
-
3
- - Hierarchy: opaque readable surfaces, semantic layering, shadows reserved for floating elements.
4
- - Color: neutral accent; on-accent follows theme; status has text/icon; no decorative status colors.
5
- - Contrast: measure foreground/background combinations. Normal text target 4.5:1; meaningful control boundaries 3:1. Do not assume an arbitrary pairing passes because individual tokens exist.
6
- - Type: serif for editorial titles/prose; sans for interaction; tabular figures for comparable data. Preserve readable sizes and wrapping.
7
- - Layout: 4-unit spacing, platform-appropriate density, concentric nested radii, no clipped essential content.
8
- - Interaction: focus visibility, keyboard order, actionable labels, loading/error/disabled state semantics, adequate hit areas.
9
- - Accessibility: test both themes, reduced motion/transparency, text scaling and non-color status cues.
10
- - Copy: direct Chinese, verb-first action labels, spaces between Chinese and Latin/numbers, concrete recovery guidance.
11
- - Integration: shared tokens and components, no duplicated literal palette or independent skill drift.
12
-
13
- Report severity, location, user impact and a specific correction. Distinguish observed failures from unverified concerns. A review does not authorize publishing, broad restyling, or changing deployment targets.
1
+ # 审查清单
2
+
3
+ 用来检查一个界面是否符合 Deernova。先确认它属于哪个场景(macOS、iOS、iPadOS、Web 应用、官网与文档),再按下面的顺序逐项看,只报告实际发现的问题。能量的地方实际去量,不要凭感觉。
4
+
5
+ ## 检查项
6
+
7
+ - **场景与密度**:用的是这个场景对应的密度。应用界面没有用官网的尺寸,触屏上没有用桌面尺寸。Web 应用在触屏设备上切到了 `touch`。macOS 的系统控件保持系统尺寸,只有自己画的内容区用桌面 token。
8
+ - **控件高度**:实际测量默认按钮、输入框、列表行、顶栏、单行 Composer 的高度,等于对应 token(Web 应用桌面端是 28、28、32、44、44)。按钮的上下内边距是 0,文字行高是 1,高度不随页面行高变化。
9
+ - **点击区域**:可见尺寸小于 `hit-min` 的控件,有透明的命中区补足(Web 用 `::before`,SwiftUI 用 `.buttonStyle` 之后的 `.padding` 加 `.contentShape`);没有为了凑点击区域把可见的控件做大。相邻控件的命中区不重叠。
10
+ - **层级与玻璃**:内容在不透明的纸面上。SwiftUI 端的侧边栏、工具栏、标签栏用系统玻璃,没有被实色背景盖掉;Web 应用里没有玻璃和模糊效果,操作层是不透明平面加 1px 线(官网页眉除外)。玻璃上没有段落和列表,没有玻璃叠玻璃;侧边栏比内容区亮一层。
11
+ - **颜色**:只有黑白灰;`accent` 跟随 `ink`,主按钮文字用 `on-accent`(深色主题下是黑字);彩色只出现在状态上,并且同时有图标或文字;没有写死的十六进制颜色。
12
+ - **对比度**:实际测量前景和背景的组合,正文不低于 4.5:1,输入框边框等有意义的控件边界不低于 3:1。不要因为用了 token 就假定一定达标,要看它放在哪种纸面上。
13
+ - **线条与阴影**:分组靠 1px 线和间距;卡片没有阴影;阴影只出现在菜单、弹窗、浮动面板和 Composer 上;虚线只用在拖放区和占位块上。
14
+ - **字体**:界面文字和 AI 回答是无衬线;衬线只出现在大标题、空状态标题、官网和文章类长文;代码是等宽;字号来自角色变量(`--type-*`、`.deernovaX`);iOS 用文本样式,没有写死字号;对齐的数字用等宽数字。
15
+ - **字重**:粗体只出现在真正的标题上;分组标题、会话名、顶栏标题、说明是常规字重,靠灰度区分。
16
+ - **间距与圆角**:间距在 4pt 网格上;组与组之间至少是组内的 2 倍;圆角来自组件的 `*-radius` token,嵌套圆角同心;阅读栏宽不超过 `content-max`。
17
+ - **交互**:一屏最多一个主按钮;按钮文字以动词开头;有悬停、按下、选中、禁用、加载、空、错误这些需要的状态;禁用和加载时布局不跳动;键盘焦点清楚。
18
+ - **动效**:时长来自 `duration-*`;动效说明因果;没有无目的的循环;减弱动态效果时只剩淡入淡出;切换内容时阅读位置不跳。
19
+ - **无障碍**:两套主题都看过;最大字号或 200% 缩放下不截断;只有图标的按钮有标签;降低透明度时玻璃变成实色。
20
+ - **文案**:用“你”;没有感叹号和 emoji;完成态用「已保存」;错误说明原因和解决办法;中文与英文、数字之间有半角空格。
21
+
22
+ ## 怎么汇报
23
+
24
+ 每个问题写清楚:严重程度(阻塞 / 明显 / 细节)、位置(文件和行号,或页面上的位置)、对用户的影响、具体怎么改(用哪个 token 或哪段参考写法)。把亲眼确认或实际测量过的问题,和推测的问题分开写。
25
+
26
+ 审查不等于授权重做:只改被要求的范围,不顺手改动无关页面,也不提高项目的最低系统版本。
@@ -0,0 +1,61 @@
1
+ # iOS
2
+
3
+ iPhone 应用用**触控**密度(`touch`):正文 17pt、点击区域至少 44pt,按钮看起来只有 36pt。SwiftUI 的通用写法见 [SwiftUI 共用](swiftui.md)。
4
+
5
+ ## 页面骨架
6
+
7
+ 两种典型页面:列表页和对话页。效果见 `assets/screens/iphone-screens-light.png` 和 `iphone-screens-dark.png`。
8
+
9
+ **列表页**
10
+
11
+ - 衬线大标题(`type-display`,也就是 `.largeTitle` 的衬线版),内容是 `card-radius` 的卡片,左右边距 `page-margin`,卡片之间 `space-2`。
12
+ - 底部是玻璃标签栏,搜索单独成为右侧的圆形玻璃按钮(`Tab(role: .search)`)。向下滚动时标签栏收起。
13
+
14
+ **对话页**
15
+
16
+ - 顶部的返回和更多按钮是系统导航栏提供的玻璃圆形按钮,标题用 `headline`。
17
+ - 对话内容:AI 回答用 `type-body`(`.body`,无衬线),用户消息在 `surface-raised` 气泡里,字号 `message-font-size`。
18
+ - 底部的 Composer 收成单行胶囊(`composer-radius`),用 `.safeAreaInset(edge: .bottom)` 悬浮在内容上方;输入多行时向上增长。
19
+
20
+ ```swift
21
+ NavigationStack {
22
+ ScrollView { ThreadView().padding(.horizontal, Deernova.pageMargin) }
23
+ .safeAreaInset(edge: .bottom) { Composer().padding(.horizontal, Deernova.space3) }
24
+ .navigationTitle("周报整理")
25
+ .navigationBarTitleDisplayMode(.inline)
26
+ }
27
+ .background(.canvas)
28
+ ```
29
+
30
+ ## 系统部分长什么样(iOS 26)
31
+
32
+ 导航栏按钮和标签栏由系统绘制,代码里不要自己设尺寸。下面是按 Apple HIG 里 iOS 26 插图量出的比例(以状态栏「9:41」的字高为尺子,屏幕宽 402),截图 `assets/screens/iphone-screens-*.png` 就是按这个比例画的:
33
+
34
+ - 顶部的玻璃按钮约 44,圆形;几个同类按钮共用一块胶囊;竖直中心离屏幕顶边约 85。
35
+ - 标签栏是悬浮的玻璃胶囊,高约 62,离屏幕左、右、下边各约 25;选中项是填满标签栏高度的胶囊(`fill-selected`),图标约 24、文字约 10。
36
+ - 搜索用 `Tab(role: .search)`,是标签栏右边单独的 62 圆形玻璃按钮。
37
+ - 底部有标签栏时,内容最后一项要留出标签栏的高度,不要被挡住。
38
+
39
+ ## 尺寸和点击区域
40
+
41
+ - 按钮的可见高度:小 30、默认 36、通栏 46;点击区域补足到 44(`hit-min`)。按钮尺寸怎么选,见 [按钮](../components/button.md)。三种尺寸放在手机里的样子见 `assets/screens/iphone-button-sizes-light.png`。
42
+ - 补点击区域的写法:在 `.buttonStyle()` 之后加 `.padding()`(这时是透明的),再加 `.contentShape(.rect)`。
43
+ - 列表行、输入框至少 44(`row-height`、`field-height`)。
44
+ - 卡片和分组列表圆角 12,底部弹窗圆角 24。
45
+
46
+ ## 字体
47
+
48
+ 一律用文本样式(`.body`、`.headline`,或生成的 `.deernovaBody`),不要写死 `size:`,否则动态字体不生效。在最大字号下也不截断文字:允许换行,横排放不下就改成竖排(`ViewThatFits`)。
49
+
50
+ ## 交互
51
+
52
+ - 只能点按钮发送消息;换行键就是换行。
53
+ - 操作按钮一直显示,不依赖悬停。
54
+ - 可以滑动删除、长按出菜单(`.swipeActions`、`.contextMenu`),但同样的操作要有可见的入口。
55
+ - 底部弹窗用 `.presentationDetents`,确认类二选一用 `.confirmationDialog`。
56
+
57
+ ## 不要
58
+
59
+ - 写死字号让动态字体失效。
60
+ - 把 Mac 的 24pt 控件直接搬到触屏上;或者反过来,为了凑 44 把按钮做成 44 高。
61
+ - 给导航栏、标签栏加实色背景。
@@ -0,0 +1,42 @@
1
+ # iPadOS
2
+
3
+ iPad 应用和 iPhone 一样用**触控**密度(`touch`),导航结构用系统的,自带 Liquid Glass:手指仍然是主要的输入方式,控件和点击区域与 iOS 相同。不同的是布局:iPad 有侧边栏和分栏,也经常接触控板和键盘。SwiftUI 的通用写法见 [SwiftUI 共用](swiftui.md)。
4
+
5
+ 这是首版规则,还没有配套的截图,落地后要在真机上检查。
6
+
7
+ ## 布局
8
+
9
+ - 全屏和大窗口时用 `NavigationSplitView`:侧边栏(`sidebar-width`,320)加内容区,必要时再加一栏详情。侧边栏是系统的玻璃面板。
10
+ - 窗口变窄(台前调度、分屏)时,`horizontalSizeClass` 变成 `.compact`,按 iPhone 的规则排版:侧边栏收起,导航改成栈。不要按设备型号判断。
11
+ - 内容区的阅读栏宽不超过 `content-max`,在宽屏上居中,两侧留白;不要把文字拉满整个横屏。
12
+ - 卡片网格按可用宽度自动增减列数(`LazyVGrid` 加 `.adaptive(minimum:)`),间距 `card-gap`。
13
+ - 弹窗用 `.presentationSizing(.form)` 或 `.page`,不要在大屏上弹出撑满全屏的底部弹窗。
14
+
15
+ ```swift
16
+ NavigationSplitView {
17
+ SidebarView()
18
+ .navigationSplitViewColumnWidth(min: 260, ideal: Deernova.sidebarWidth, max: 400)
19
+ } detail: {
20
+ ScrollView { ThreadView().frame(maxWidth: Deernova.contentMax) }
21
+ .safeAreaInset(edge: .bottom) { Composer().frame(maxWidth: Deernova.contentMax).padding(Deernova.pageMargin) }
22
+ }
23
+ .background(.canvas)
24
+ ```
25
+
26
+ ## 尺寸
27
+
28
+ - 和 iOS 相同:按钮可见 30 / 36 / 46,点击区域 44,列表行和侧边栏项 44,正文 17pt(动态字体)。
29
+ - 侧边栏项沿用 [侧边栏](../components/sidebar.md) 的样式,触控密度下行高自动变成 44。
30
+
31
+ ## 指针和键盘
32
+
33
+ - 接触控板时,可点的自定义视图加 `.hoverEffect()`,让指针吸附和高亮跟系统一致。
34
+ - 常用操作加 `.keyboardShortcut`,按住 Command 时系统会列出它们。Return 在 Composer 里的行为和 Mac 一致:接了硬件键盘时 Return 发送、Shift-Return 换行。
35
+ - 支持拖放(`.draggable`、`.dropDestination`):文件拖进 Composer 就是添加附件。拖放区用 `line-style-dropzone` 的虚线。
36
+ - 右键(双指轻点)出 `.contextMenu`。
37
+
38
+ ## 不要
39
+
40
+ - 按设备型号写死布局;用尺寸类别和可用宽度。
41
+ - 在大屏上把正文拉满整行。
42
+ - 把桌面尺寸用在 iPad 上:即使接了触控板,用户也随时会用手指点。
@@ -0,0 +1,65 @@
1
+ # macOS
2
+
3
+ Mac 应用分两部分处理:
4
+
5
+ - **系统的部分**(侧边栏、工具栏、菜单、设置窗口、表单、弹窗):直接用系统控件和系统尺寸,自带 Liquid Glass。不要盖掉玻璃,也不要硬改成 Web 的尺寸。
6
+ - **自己画的内容区**(对话、卡片、Composer、任务清单):用**桌面**密度(`desktop`),和 Web 应用一致:正文 15/24、卡片圆角 12、Composer 单行 44、按钮 28。
7
+
8
+ SwiftUI 的通用写法见 [SwiftUI 共用](swiftui.md)。
9
+
10
+ ## 窗口骨架
11
+
12
+ 标准结构:左边是悬浮的玻璃侧边栏,右边内容区的上方浮着玻璃工具栏,下方浮着 Composer,需要时右边打开 inspector 侧面板。效果见 `assets/screens/mac-window-light.png` 和 `mac-window-dark.png`:系统的部分按 Apple HIG 里 macOS 26 的比例画,内容区用 Deernova 的组件。
13
+
14
+ - 窗口底色 `canvas`。侧边栏是 `NavigationSplitView` 的 sidebar 列,系统会渲染成悬浮的玻璃面板,默认宽度参考 `sidebar-width`(288)。
15
+ - 工具栏用 `.toolbar` 声明,标题用 `headline`;右侧的按钮组由系统渲染成玻璃胶囊。内容从工具栏下面滚过去。
16
+ - 内容区居中,阅读栏宽不超过 `content-max`,左右边距 `page-margin`。
17
+ - 底部的 Composer 悬浮在内容之上,它承载输入的内容,所以是纸面,不是玻璃。用 `.safeAreaInset(edge: .bottom)` 放置,让内容可以滚到它的上方。
18
+
19
+ ```swift
20
+ NavigationSplitView {
21
+ SidebarView()
22
+ .navigationSplitViewColumnWidth(min: 180, ideal: Deernova.sidebarWidth, max: 300)
23
+ } detail: {
24
+ ScrollView { ThreadView().frame(maxWidth: Deernova.contentMax) }
25
+ .safeAreaInset(edge: .bottom) { Composer().padding(Deernova.pageMargin) }
26
+ .navigationTitle(chat.title)
27
+ .toolbar {
28
+ ToolbarItemGroup {
29
+ ShareLink(item: chat.url)
30
+ Menu("更多", systemImage: "ellipsis") { … }
31
+ }
32
+ }
33
+ }
34
+ .background(.canvas)
35
+ ```
36
+
37
+ ## 系统部分长什么样(macOS 26)
38
+
39
+ Apple 没有公布工具栏按钮的点数,大小由系统决定,代码里不要自己设。下面是按 Apple HIG「Toolbars」页里 macOS 26 备忘录截图量出的比例(以红绿灯直径 12、间距 20 为尺子),用来画设计稿和检查实现:
40
+
41
+ - 工具栏按钮约 34 × 34,图标约 16;竖直中心和红绿灯在同一条线上,离窗口顶边约 22。
42
+ - 同类按钮共用一块玻璃胶囊(系统按控件类型自动分组),胶囊和按钮一样高,没有额外的内边距;一个工具栏最多三组。
43
+ - 符号不加外圈(不用 `circle` 版本的 SF Symbol),悬停和选中状态由系统画。
44
+ - 只有一个主操作用 `.prominent` 样式,放在最右边。
45
+ - 窗口圆角约 22;悬浮的玻璃侧边栏和窗口同心(内缩 8,圆角约 14)。
46
+ - 标题不超过 15 个字,可以带一行灰色的副标题;不要用应用名当标题。
47
+
48
+ ## 尺寸
49
+
50
+ - 系统控件:用系统按钮加 `.controlSize(…)`、系统 `List`,尺寸交给系统。
51
+ - 自己画的内容区:用 `Deernova.*`(macOS 上是桌面密度):按钮 28(`buttonHeightMd`)、卡片圆角 12、内边距 12 × 16、Composer 单行 44、正文 `.deernovaBody`(15)。
52
+ - 图标按钮至少 24×24(`hit-min`)。
53
+
54
+ ## 交互
55
+
56
+ - Return 发送、Shift-Return 换行;常用操作配键盘快捷键(`.keyboardShortcut`),并显示在菜单里。
57
+ - 悬停才显示的次要操作(消息下的复制、重新生成),键盘聚焦时也要显示。
58
+ - 设置窗口用 `Settings` 场景加 `Form` 和 `.formStyle(.grouped)`。
59
+ - 右键菜单(`.contextMenu`)里的操作和工具栏、菜单栏一致。
60
+
61
+ ## 不要
62
+
63
+ - 把 iOS 的 36pt 按钮、44pt 列表行搬到 Mac 上;或者为了和 Web 一样,把系统控件改成自绘。
64
+ - 给侧边栏或工具栏加实色背景,盖掉系统玻璃。
65
+ - 自己画窗口标题栏和红绿灯按钮。
@@ -0,0 +1,94 @@
1
+ # SwiftUI 共用
2
+
3
+ macOS、iOS 和 iPadOS 共用的 SwiftUI 落地方法。各平台的骨架和尺寸见 [macOS](macos.md)、[iOS](ios.md)、[iPadOS](ipados.md)。
4
+
5
+ ## 系统版本
6
+
7
+ Deernova 按 iOS 26 / iPadOS 26 / macOS 26 设计,Liquid Glass 相关的 API(`.glassEffect()`、`GlassEffectContainer`、`.buttonStyle(.glass)` 等)从这些版本开始提供。
8
+
9
+ - 先看项目的部署目标。不要为了用这份规范去提高项目的最低系统版本。
10
+ - 部署目标低于 26 时,系统导航栏、工具栏、标签栏会自动使用当时的材质,不用处理;自定义的浮动按钮用 `if #available(iOS 26, macOS 26, *)` 分支,旧系统上退回 `.background(.regularMaterial, in: .capsule)`。
11
+ - 这里的示例是设计参考,落地后要在项目的 SDK 下编译,并在真机或模拟器上看效果。
12
+
13
+ ## 接入变量
14
+
15
+ 把 `assets/DeernovaTokens.swift` 加进 App target。它由 `tokens.json` 生成,提供:
16
+
17
+ - **颜色**:`.canvas`、`.surface`、`.ink`、`.inkSecondary`、`.line`、`.brandAccent`、`.success` 等,跟随浅色和深色外观,可以直接写 `.foregroundStyle(.inkSecondary)`、`.background(.surface, in: …)`。`accent` 命名为 `brandAccent`,避开系统的 `accentColor`。不需要 Asset Catalog。
18
+ - **尺寸**:`Deernova.buttonHeightMd`、`Deernova.cardRadius`、`Deernova.composerMinHeight`、`Deernova.pageMargin` 等。macOS 上是 `desktop` 的值(只用于自己画的内容区,系统控件保持系统尺寸),iOS 和 iPadOS 上是 `touch` 的值。
19
+ - **字体**:`.deernovaBody`、`.deernovaTitle`、`.deernovaProse` 等。macOS 上是固定字号(正文 15),iOS 上是文本样式,支持动态字体。
20
+
21
+ 项目已经用 Asset Catalog 管理颜色时,也可以按 token 名建 Color Set、分别填入 Any 和 Dark 两套值(见 [设计变量](../tokens.md)),再写同名的扩展。`glass-fill`、`glass-stroke` 只用于网页,App 里不要建。
22
+
23
+ 在 App 入口统一着色,系统控件会自动跟随(按钮、选中态在浅色主题变黑,在深色主题变白)。开关单独着色:
24
+
25
+ ```swift
26
+ WindowGroup {
27
+ RootView()
28
+ .tint(.brandAccent)
29
+ }
30
+
31
+ Toggle("iCloud 同步", isOn: $sync)
32
+ .tint(.success)
33
+ ```
34
+
35
+ 窗口和页面底色用 `.background(.canvas)`;系统列表要先 `.scrollContentBackground(.hidden)`,底色才会生效。
36
+
37
+ ## 字体
38
+
39
+ | 角色 | macOS | iOS / iPadOS |
40
+ | --- | --- | --- |
41
+ | `display` | `.system(size: 26, weight: .semibold, design: .serif)` | `.system(.largeTitle, design: .serif).weight(.semibold)` |
42
+ | `title` | `.system(size: 18, weight: .semibold, design: .serif)` | `.system(.title3, design: .serif).weight(.semibold)` |
43
+ | `headline` | `.system(size: 15, weight: .semibold)` | `.headline` |
44
+ | `body`(界面正文、AI 回答) | `.system(size: 15)` 加 `.lineSpacing(6)` | `.body` |
45
+ | `prose`(文章类长文) | `.system(size: 16, design: .serif)` | `.system(.body, design: .serif)` 加 `.lineSpacing(6)` |
46
+ | `callout` / `footnote` / `caption` | `.system(size: 14)` / `13` / `12` | `.callout` / `.footnote` / `.caption` |
47
+ | `code` | `.system(size: 13, design: .monospaced)` | `.system(.footnote, design: .monospaced)` |
48
+
49
+ 生成的 `.deernovaX` 就是这张表。系统控件(`List`、`Form`、按钮)保持系统字号,不要套这些字体。计数、价格、时间这类需要对齐的数字加 `.monospacedDigit()`。中文会自动使用苹方。
50
+
51
+ ## 点击区域
52
+
53
+ 可见高度和点击区域分开处理:按钮看起来是 `Deernova.buttonHeightMd`,再在 `.buttonStyle()` **之后**加 `.padding()` 和 `.contentShape(.rect)`,把可点范围补足到 `Deernova.hitMin`。这时的 padding 在按钮背景外面,是透明的。写法见 [按钮](../components/button.md)。
54
+
55
+ 只有图标的按钮用 `Button("搜索", systemImage: "magnifyingglass")` 加 `.labelStyle(.iconOnly)`,文字会自动成为无障碍标签。
56
+
57
+ ## 细线
58
+
59
+ 自己画的边框(卡片、Composer、任务清单)用 1pt 的 `Deernova.strokeLine`:
60
+
61
+ ```swift
62
+ RoundedRectangle(cornerRadius: Deernova.cardRadius)
63
+ .strokeBorder(.line, lineWidth: Deernova.strokeLine)
64
+ ```
65
+
66
+ 系统的 `Divider()` 和 `List` 的行分隔保持系统样式,不去改。
67
+
68
+ ## 玻璃
69
+
70
+ - 导航栏、工具栏、标签栏、侧边栏、inspector:用标准的 `NavigationStack`、`NavigationSplitView`、`TabView`、`.toolbar`、`.inspector`,系统会自动渲染成玻璃,不要给它们加背景。玻璃是原生端的优势,Web 应用没有。
71
+ - 浮动按钮:`.buttonStyle(.glass)`;主操作用 `.buttonStyle(.glassProminent)` 加 `.tint(.brandAccent)`,一屏最多一个。
72
+ - 相邻的几个玻璃元素放进同一个 `GlassEffectContainer`,它们会互相融合,切换时也会跟着变形。
73
+ - 完全自定义形状时用 `.glassEffect(.regular, in: .capsule)`;触控端可点击的玻璃加 `.interactive()`,按下时有形变反馈。
74
+ - iOS 标签栏在向下滚动时收起:`.tabBarMinimizeBehavior(.onScrollDown)`;搜索用 `Tab(role: .search)`。
75
+ - Mac 上侧边栏旁边的大图,用 `.backgroundExtensionEffect()` 延伸到侧边栏下方。
76
+ - 嵌套的圆角用 `ConcentricRectangle()`,内层会自动和外层容器同心。
77
+
78
+ ## 动效
79
+
80
+ - 默认 `.spring(response: 0.35, dampingFraction: 0.85)`;即时反馈用 `.easeOut(duration: Deernova.durationInstant)`,展开和切换用 `Deernova.durationBase`。弹窗、导航的转场交给系统。
81
+ - 读取 `@Environment(\.accessibilityReduceMotion)`,开启时把位移、缩放换成 `.opacity` 转场。
82
+
83
+ ## 无障碍
84
+
85
+ - `@Environment(\.accessibilityReduceTransparency)`:开启时,自己写的半透明效果换成 `.surfaceRaised` 实色。系统玻璃会自动处理。
86
+ - `@Environment(\.dynamicTypeSize)`:到 `.accessibility1` 以上时,横排放不下的内容改成竖排(`ViewThatFits` 或 `AnyLayout`)。
87
+ - 状态用图标加文字,不只靠颜色。
88
+
89
+ ## 验证
90
+
91
+ - 每个新界面都在 `#Preview` 里同时看浅色和深色:`.preferredColorScheme(.dark)`。
92
+ - iOS 用最大的动态字体看一遍,确认没有文字被截断。
93
+ - 用 VoiceOver 或无障碍检查器确认只有图标的按钮都有标签。
94
+ - 在系统设置里打开「降低透明度」和「减弱动态效果」各看一次。
@@ -0,0 +1,34 @@
1
+ # 官网与文档
2
+
3
+ 官网、产品介绍页、文档和规范页这类以阅读为主的页面,用**舒展**密度(`comfortable`):字号和留白更大,控件只用于少量导航。仍然只用黑白灰、细线分组、衬线标题。
4
+
5
+ 应用界面(后台、工具、编辑器)不要用这套尺寸,见 [Web 应用](web.md)。
6
+
7
+ ## 启用
8
+
9
+ 在 `<html>` 上设置 `data-density="comfortable"`,整页的字号、按钮、间距 token 都会换成舒展的值。只有某一块是阅读页面时(例如应用里嵌的帮助文档),把属性加在那一块的容器上。
10
+
11
+ ## 版式
12
+
13
+ - 大标题用 `type-display`(衬线)。首屏标题可以再放大,用 `clamp()` 跟随视口,例如 `font-size: clamp(40px, 6vw, 88px)`,但每页只有一个。
14
+ - 正文 `type-body`,长文 `type-prose`,栏宽不超过 `content-max`。
15
+ - 章节之间 `gap-group`(48),首屏上下留白 `space-16`。
16
+ - 一页只用一套网格;左右边距 `page-margin`,宽屏时内容不超过 `breakpoint-lg` 宽,两侧留白。
17
+
18
+ ## 控件
19
+
20
+ - 按钮是胶囊形(`button-radius`),大号 44 高,首屏的主要操作用它,最多两个并排:一个实色、一个描边。
21
+ - 页眉是吸顶的玻璃条,只放字标、几个链接和外观切换;图标按钮的点击区域是 `glass-button-size`(桌面 32、触屏 44),悬停底色比它小一圈。
22
+ - 文档页左侧可以有目录(`sidebar-width`),当前位置用 `fill-selected` 标出。窄于 `breakpoint-md` 时目录收起。
23
+
24
+ ## 动效
25
+
26
+ 品牌展示页可以有叙事动效(首页的麋鹿),但要能暂停、不妨碍阅读;减弱动态效果时保持静止。打字效果、滚动视差这类装饰只用一次,不要每个区块都动。
27
+
28
+ 插画默认是黑白的。颜色只能作为用户触发、几秒后自行褪回黑白的插画效果出现(首页点鹿后场景上色);页眉、文字、按钮和链接始终用黑白设计变量,不跟着变色。
29
+
30
+ ## 不要
31
+
32
+ - 把这里的 44px 胶囊按钮和大字号搬进应用界面。
33
+ - 为了“高级感”加渐变、常驻的彩色插画或大面积阴影;让颜色进入页眉、文字和按钮。
34
+ - 把给 agent 或开发者看的安装说明放在首屏;放到页面末尾或单独的资源页。
@@ -0,0 +1,83 @@
1
+ # Web 应用
2
+
3
+ 后台、工具、编辑器、AI 对话这类在浏览器里使用的应用。桌面上是**桌面**密度(`desktop`),同一套代码在触屏设备上自动切到**触控**密度(`touch`)。官网、文档这类阅读页面见 [官网与文档](web-marketing.md)。
4
+
5
+ 骨架和密度参照 Claude 桌面端(Code):不透明的平面、1px 分隔线、正文 15/24。Web 应用**不用玻璃**,也不用模糊效果去模仿。
6
+
7
+ 不需要安装任何 npm 包。
8
+
9
+ ## 接入变量
10
+
11
+ `assets/tokens.css` 是现成的 CSS 变量,包含两套主题和四种密度:
12
+
13
+ - **主题**:浅色是默认值;系统切到深色时自动换成深色。在 `<html>` 上设置 `data-theme="light"` 或 `"dark"` 可以强制指定,去掉属性就跟随系统。属性也可以放在任何元素上,只影响它里面的内容。
14
+ - **密度**:默认 `desktop`;`@media (pointer: coarse)` 的设备自动换成 `touch`。在 `<html>` 或任何元素上设置 `data-density="desktop" | "touch" | "comfortable"` 可以强制指定。
15
+
16
+ 接入方式按项目现有的体系选:
17
+
18
+ - 项目里还没有设计变量时,把 `tokens.css` 复制到样式目录,在全局样式里引入一次,**不要放进 `@layer`**。
19
+ - **Tailwind v4**:再复制 `assets/tailwind.css`,依次引入 `tailwindcss`、`tokens.css`、`tailwind.css`。之后可以写 `bg-surface text-ink border-line`、`text-body`、`h-control-md px-button-x-md rounded-button`、`shadow-float`,主题和密度切换都自动生效。
20
+ - **Tailwind v3**:在 `tailwind.config.js` 里把颜色和尺寸指向 CSS 变量:
21
+
22
+ ```js
23
+ export default {
24
+ theme: {
25
+ extend: {
26
+ colors: { canvas: 'var(--canvas)', surface: 'var(--surface)', ink: 'var(--ink)', line: 'var(--line)', accent: 'var(--accent)', 'on-accent': 'var(--on-accent)' /* …其余语义颜色同理 */ },
27
+ spacing: { 'control-sm': 'var(--button-height-sm)', 'control-md': 'var(--button-height-md)', 'control-lg': 'var(--button-height-lg)', field: 'var(--field-height)', row: 'var(--row-height)' },
28
+ borderRadius: { button: 'var(--button-radius)', field: 'var(--field-radius)', card: 'var(--card-radius)' },
29
+ fontFamily: { serif: 'var(--font-serif)', sans: 'var(--font-sans)', mono: 'var(--font-mono)' }
30
+ }
31
+ }
32
+ };
33
+ ```
34
+
35
+ - **CSS-in-JS 或组件库主题**:把 Deernova 的值映射进去,值写成 `var(--name)`,保持语义名,不要在组件里直接写十六进制颜色和尺寸数字。
36
+
37
+ ## 控件高度:最常见的问题
38
+
39
+ Web 上按钮“上下特别宽”几乎都是同一个原因:用内边距和行高撑出高度。规则:
40
+
41
+ 1. **高度用 token**:`height: var(--button-height-md)`(输入框 `--field-height`,列表行 `min-height: var(--row-height)`)。
42
+ 2. **上下内边距为 0**:`padding: 0 var(--button-padding-x-md)`。
43
+ 3. **行高为 1,不继承页面**:`font: var(--button-font-weight) var(--button-font-size-md)/1 var(--font-sans)`。页面正文的 1.6–1.8 行高一旦进到按钮里,按钮就会变高。
44
+ 4. **点击区域单独补**:用 `::before` 伪元素把命中区撑到 `max(100%, var(--hit-min))`。**不要用 padding 补**,CSS 的 padding 在背景里面,会让可见的按钮一起变大。SwiftUI 示例里的 `.padding()` 写在 `.buttonStyle()` 之后,是透明的,不能照搬到 CSS。
45
+ 5. **不要套用官网的按钮**:官网的 44px 胶囊按钮属于 `comfortable` 密度,不用在应用里。
46
+
47
+ 完整写法见 [按钮](../components/button.md)。做完之后在浏览器里量一下:桌面上默认按钮的实际高度应该等于 28,侧边栏行 32,顶栏 44。
48
+
49
+ ## 参考写法
50
+
51
+ 每个组件说明里的 CSS 是可以直接对照的参考实现,类名以 `dn-` 开头。它们不是要引入的样式库:项目有自己的组件时,按里面的数值和状态改写;类名换成项目自己的。示例 HTML 里的 `dn-row`、`dn-stack` 只是排版辅助。
52
+
53
+ ## 页面骨架
54
+
55
+ 写法见 [顶栏与侧面板](../components/toolbar.md)。
56
+
57
+ - **侧边栏**:宽 `sidebar-width`(288),`surface` 底,比内容区亮一层,右边一条 1px 线。结构见 [侧边栏](../components/sidebar.md)。
58
+ - **顶栏**:高 `toolbar-height`(44),`canvas` 底,不透明,底部一条 1px 线;左边是标题,右边是一排 28 的图标按钮。
59
+ - **内容区**:`canvas` 底;对话栏宽度不超过 `content-max`,在内容区居中;段落、工具调用行、卡片、任务清单之间 `space-4`。AI 回答用 `type-body`,不用衬线。
60
+ - **底部**:[Composer](../components/composer.md) 悬浮在内容之上,上面可以贴一条提示,下面是模型和权限的信息行;内容区底部留出它的高度。
61
+ - **侧面板**:右边可以并排打开终端、预览等面板,标签头和顶栏同高。
62
+ - 窄于 `breakpoint-md`(960)时侧面板变成抽屉;窄于 `breakpoint-sm`(760)时侧边栏也收起成抽屉,顶栏左侧放菜单按钮。不能出现横向滚动。
63
+
64
+ ## 字体
65
+
66
+ 直接用 `--font-serif`、`--font-sans`、`--font-mono`,都是系统字体,不需要加载网络字体。字号用 `font: var(--type-body)` 这类角色变量,见 [字体](../foundations/typography.md)。
67
+
68
+ ## 控件
69
+
70
+ - 用原生元素:`<button>`、`<a>`、`<input>`、`<select>`、`<dialog>`、`<details>`。只有图标的按钮加 `aria-label`。
71
+ - 焦点环:`outline: var(--stroke-focus) solid var(--focus-ring); outline-offset: var(--focus-offset);`,只在 `:focus-visible` 时显示。
72
+ - 细线:`border: var(--stroke-line) solid var(--line);`。
73
+ - 分段控件、开关这类有状态的控件,用 `aria-pressed`、`role="switch"` 或 `role="tab"` 标出当前状态。
74
+ - 弹窗用 `<dialog>` 的 `showModal()`,自带焦点管理和 Esc 关闭。
75
+ - 悬停效果放在 `@media (hover: hover)` 里,触屏上不会卡在悬停状态。
76
+
77
+ ## 验证
78
+
79
+ - 375px 和桌面两种宽度,浅色和深色两套主题;用开发者工具切到触屏模拟一次,确认密度换成 `touch`。
80
+ - 量一下默认按钮、输入框、列表行的实际高度,应该等于对应 token。
81
+ - 只用键盘走一遍:Tab 顺序合理,焦点环清楚,Esc 能关闭弹层。
82
+ - 浏览器缩放到 200%,长中文、长英文和空数据都不溢出、不重叠。
83
+ - 打开「减弱动态效果」和「降低透明度」各看一次。