- doc/dev/design.md:把牛奶布艺设计语言正式沉淀为规范——设计哲学、令牌表(色彩/圆角/ 阴影高光/动效曲线,含深浅色)、布局与三 Tab 信息架构、逐组件规范(卡片/按钮三级/输入框/ 徽标体系/分段 Tab/折叠菜单/空状态/Toast/进度/模态)、动效、无障碍、文案、反例、落地清单。 令牌与 class 名与 styles.css 一一对应,是可执行的活规范而非抽象理论。 - 无障碍基线(此前为 0):全局 :focus-visible 绿色焦点环(仅键盘导航出现,鼠标不触发; 无边框拟物 UI 尤其需要);prefers-reduced-motion 降级(去装饰动效、保留 spinner/进度的 必要循环)。已用真实构建 CSS 截图验证焦点环不被按钮 overflow:hidden 裁剪、贴合胶囊圆角。
10 KiB
设计规范 · 云微面板设计系统
本文是面板前端(
panel/web)的设计语言与组件规范,与代码一一对应——令牌就是styles.css里的 CSS 变量,组件名就是真实的 class。改 UI 前先读这里;新增组件按此规范落地,别另起炉灶。 配套:架构层规范见 架构守则.md。
一、设计哲学
三条主线,缺一不可:
- 牛奶布艺(soft neumorphism) —— 整个界面是盖在桌面上的一整块柔软白布,每个组件是被布从下方顶起的物体。这个比喻决定一切视觉:
- 高光在物体顶面(radial 渐变,中心偏上)——布料最贴、最受光处;
- 阴影是布料折痕(小半径、紧贴边缘、冷灰蓝),不是悬浮投影;
- 组件亮度 ≥ 背景(越靠近光源越亮);
- 层级靠明度对比,不靠边框/分割线——全站几乎没有
border。
- 微信绿主色 ——
#07C160。主按钮、选中态、强调文字、焦点环都用它。用户对这个绿有肌肉记忆,别换色相。 - iOS 式饱满圆角 + 克制 —— 圆角只有大/中/小三档 + 胶囊;文案精炼、层级不超过 4 级字号。
一句话检验:任何新组件放上去,要像「同一块布下顶起的又一个物体」,而不是「贴上去的一张卡」。
二、设计令牌(:root,深色见 [data-theme='dark'])
改样式只用令牌,不写死颜色/阴影。
色彩
| 令牌 | 浅色 | 深色 | 用途 |
|---|---|---|---|
--base |
#ebedf1 |
#14161c |
整块布底色(页面背景) |
--surface |
#ffffff |
#23262f |
浮起表面(卡片/按钮 fill)——必须 ≥ base 亮度 |
--trough |
#e0e3ea |
#0e0f14 |
凹槽(输入框/进度槽/折叠菜单条)——比 base 更暗 |
--text |
#1a1d24 |
#e7eaef |
主文字 |
--muted |
#8a9099 |
#969ca6 |
次文字(最低到此级,不用三级灰) |
--wx-green / --wx-green-dark |
#07c160 / #06ad56 |
同 | 主色 / 主色按下态 |
--danger |
#fa5151 |
同 | 危险/离线/删除 |
--shadow |
51 66 102(冷灰蓝) |
0 0 0(近纯黑) |
阴影色 RGB,配 rgba(var(--shadow)/α) |
--green-rgb / --danger-rgb |
7 193 96 / 250 81 81 |
同 | 半透强调用 |
深色不是简单反色:base 用亮蓝黑、surface 再高一档、阴影转近纯黑、顶面高光压到极淡(0.06)。
圆角
| 令牌 | 值 | 用途 |
|---|---|---|
--r-card |
28px |
卡片、进入入口、模态 |
--r-blob |
22px |
中等浮起块 |
--r-small |
16px |
按钮、输入框 |
| (胶囊) | 999px |
徽标 .tag、分段 Tab、chip |
禁止直角或 <12px 小圆角——破坏「柔软布料」质感。
阴影与高光(布料的灵魂)
| 令牌 | 含义 |
|---|---|
--crease |
浮起折痕(两层:0 1px 3px + 0 2px 8px,紧贴边缘)。所有浮起组件的基础 |
--crease-press |
按下态折痕(更紧更浅)——按下即收紧,模拟按进布里 |
--crease-accent |
绿色折痕(主按钮/Logo),阴影色用 --green-rgb |
--sheen |
顶面高光 radial(ellipse at 50% 28%,白→透明)。所有浮起组件用 ::before 叠这层,否则变平贴色块 |
硬约束:阴影 x 恒为 0(俯视对称光);y ≤ 6;第一层 radius ≤ 4、第二层 ≤ 14。radius >15 或 y >5 就成了悬浮投影,违反比喻。
动效曲线
- 主 spring:
cubic-bezier(0.2, 0.8, 0.2, 1),时长 0.16–0.2s(按钮、输入聚焦、Tab)。全站已用 12+ 处,别引入别的曲线。 - Toast 入场:
cubic-bezier(0.2, 0.9, 0.2, 1),0.32s。
三、布局与信息架构
- 页面容器
.content:移动端max-width:560px;≥880px 宽屏max-width:940px、加大内边距。始终居中。 - 区块
.section-row(标题行,space-between,左标题右操作)+.section-title(15px/700)。区块间距margin-top:22px。 - 网格
.inst-grid:repeat(auto-fill, minmax(248px, 1fr)),gap 14px,align-items:start(各卡按内容高度,避免展开菜单时同行被拉等高)。 - 三 Tab 信息架构(管理页):内容多时按用户心智分组成 Tab,而非一条长滚动。分组原则:
- 日常高频(实例)/ 偶尔配置(用户)/ 出事才看(系统:残留资源+诊断+关于);
- Tab 上用
.seg-dot红点提示「该 Tab 有待处理事项」,避免藏进 Tab 的事被漏看; - 记住上次停留的 Tab(
sessionStorage)。
四、组件规范
所有浮起组件 = surface fill + --crease + ::before 叠 --sheen。这是共用原子(.mf-raised 及 .card/.enter-card/.list/.wx-state 共享)。
浮起卡片 .card / .inst-card
圆角 --r-card,padding 18–24px,overflow:hidden(::before 高光不外溢)。展开悬浮菜单时才 overflow:visible + 抬 z-index。
按钮(三级 + 文本)
| 类 | 形态 | 用途 |
|---|---|---|
.btn |
surface 浮起 blob,按下 scale(0.97) + 折痕收紧 |
次要动作 |
.btn.btn-primary |
绿底、白字、--crease-accent、更克制的高光(别让白 sheen 冲淡绿) |
主动作,一屏一个 |
.btn.btn-danger |
红底,折痕用 danger 色 | 删除等破坏性动作 |
.btn-text |
无浮起、绿字、按下淡绿底 | 行内轻操作(+新建、改名) |
统一 height:48px、--r-small。禁用 opacity:0.6、不改阴影。
输入框 .input(凹陷,不是浮起)
trough fill + 顶部内阴影(inset),聚焦时叠 0 0 0 3px 绿色半透环。凹陷元素不画外折痕——它是被按进布里的槽,与浮起组件形成对比。
徽标体系 .tag(胶囊,white-space:nowrap)
| 变体 | 色 | 语义 |
|---|---|---|
.tag(默认) |
绿 | 中性/正向标识 |
.tag-on |
绿 | 在线/正常 |
.tag-off |
红 | 离线/已禁用 |
.tag-busy |
—— | 处理中 |
.tag-warn |
琥珀 | 可升级/需注意 |
.tag-muted |
灰 | 纯元数据(镜像版本号),tabular-nums |
布局铁律:一个组件的标题行最多放 名字 + 1 个状态徽标。其余徽标(可升级、版本…)下沉到独立的 .inst-meta 元数据行(可换行)。塞多了就会名字被截断、徽标竖排——这是踩过的坑(见卡片布局修复)。
分段 Tab .seg-tabs / .seg-tab(凹槽 + 浮起选中胶囊)
外层 trough 凹槽胶囊(无外折痕),选中项是顶起的 surface 白胶囊(--crease + 绿字)。这套「凹槽 + 浮起 bump」和输入框凹陷同构,是本设计系统区分「选中/未选中」的标准范式。
理想增强:选中指示器用 matched-geometry「滑动」而非淡入淡出(当前是 background 渐变过渡,可接受)。未来若做,用一个绝对定位的 bump +
transform过渡在选项间滑动。
折叠菜单 .inst-menu-toggle + .inst-menu
操作多时收进「管理」折叠菜单,按 运维/设置/危险 分组,危险项 .inst-menu-danger 单列。悬浮层不占文档流,点外部关闭。
空状态 .empty-state
中心 120px 的 surface 圆 blob(.empty-blob + 折痕 + 高光)+ 图标(ultraLight)+ 标题 + 副标题 + 一个主按钮。别用一行冷冰冰的「暂无数据」。
Toast .toast / 进度条 .wx-progress
Toast 右上滑入(.toast-ok/.toast-error)。进度条:确定进度用 width%,不确定用 .indeterminate 循环。二者都是必要状态反馈,在 reduced-motion 下仍保留动画。
模态 .modal-mask + .modal
遮罩点击关闭(内容 stopPropagation),.modal-actions 右对齐(取消在左、主/危险动作在右)。
五、动效规范
- 状态切换用主 spring 曲线;Tab/分段切换倾向 matched-geometry 滑动而非淡入淡出。
- 压按反馈:
scale(0.97)+ 折痕由--crease→--crease-press(0.18s)。 prefers-reduced-motion: reduce:去掉装饰性过渡/位移,但保留 spinner 与不确定进度条的循环(否则像卡死)。已在styles.css末尾统一处理,新增装饰动画自动被覆盖。
六、无障碍(基线,不是可选项)
- 键盘焦点环:全局
:focus-visible→ 2px 绿色 outline + 2px offset。只在键盘导航时出现,鼠标点击不触发。无边框的拟物 UI 尤其需要它,否则键盘用户完全看不到焦点。输入框用自带内环、不叠加。 - 对比度:文字最低到
--muted,不用三级灰(亮白 surface 上会糊)。 - 触摸目标:交互元素 ≥ 44×44(按钮 48 高达标;小图标按钮注意留足点击区)。
- 减弱动效:见上,尊重系统设置。
七、内容与文案
- 全中文;语气像同事说人话,不像系统报错。错误给「怎么办」,不只给「出错了」。
- 数字/版本号用
tabular-nums(对齐好看)。 - 破坏性操作(删除/清数据)文案明确后果 + 二次确认,危险按钮红色。
八、常见错误(反例 → 正例)
| ❌ | ✅ |
|---|---|
| 大半径(>15)+ 大 y 的 drop shadow | 小半径紧贴边缘的 --crease 折痕 |
| 组件顶部 linear 渐变高光 | 中心偏上的 radial --sheen(::before) |
| 用 border/分割线区分组件 | 靠明度(surface > base > trough)+ 折痕 |
| 组件 fill = base 或更暗 | 组件必须 --surface,亮于 base |
| 标题行塞名字+3 个徽标 | 标题行只留名字+1 状态徽标,其余进 .inst-meta |
写死 #07c160 / box-shadow: ...黑 |
用 --wx-green / --crease 令牌 |
| 直角或 8px 小圆角 | ≥16px 或胶囊 |
| 亮白 surface 上用三级灰文字 | 最低 --muted |
| 无键盘焦点样式 | 全局 :focus-visible 绿环 |
九、落地清单(改/加 UI 前逐条过)
- 只用令牌,没写死颜色/阴影/圆角;
- 浮起组件有
surfacefill +--crease+::before的--sheen; - 阴影
x=0、y≤6、无大散射;圆角 ≥16 或胶囊; - 组件亮度 ≥ base;无 border/分割线;无三级灰文字;
- 标题行 ≤ 名字 + 1 徽标,其余进元数据行;
- 深浅色都过一遍(
data-theme切换); - 键盘 Tab 能看到焦点环;
prefers-reduced-motion下无眩晕动画; - 交互目标 ≥44px;破坏性操作有确认 + 红色。