# UI 基线(CYBER 暗色单主题) 本文件是 hermes-ui 的**视觉语言唯一依据**。页面样式与本文冲突时,以本文为准; 本文没覆盖的场景,先在 `docs/arco-mapping.md` 补映射、再回本文补约定,然后才动代码。 - 样板页:`views/LoginView.vue`(装饰位面 + 表单)、`components/layout/AppSidebar.vue`(导航 + 弹窗 + tag) - 令牌源:`packages/client/src/styles/variables.scss`(CSS 变量 + SCSS 委托变量) - Arco 实际外观由 `styles/global.scss` 的 Arco token bridge 决定;`styles/arco-theme.ts` 是意图说明与测试锚点 - 组件映射:`docs/arco-mapping.md` > **历史**:本文件曾描述「黑白水墨(Pure Ink)」三主题(ink light / ink dark / comic)。 > 亮度/风格切换器已移除,暗色是唯一主题,该描述不再适用。 > 当前主题推进状态见 `docs/cyber-theme-plan.md`。 --- ## 1. 三色语义系统(最高优先级) > **每种颜色只承担一种含义,不得混用。** | 颜色 | 令牌 | 含义 | 典型用途 | |---|---|---|---| | 霓虹青 | `--accent-primary` | **系统本体** | 品牌、主按钮、当前选中、链接 | | 琥珀橙 | `--warning` | **需要你介入** | 审批、等待确认、上下文将满、中断态 | | 荧光绿 | `--success` | **机器在动** | 工具成功、流式输出、连接健康 | | 红 | `--error` | **出错** | 仅表示出错,不参与上面三色分工 | - 失败时**不要**靠「加深强调色」表达,必须换到 `--error` —— 否则四个语义挤在同一色相里,紧急程度就读不出来了。 - `--accent-info` 是第四种蓝,只在需要与青/橙/绿都区分开时用(如链接性提示)。 - 同一语义在全项目**必须用同一个令牌**。看到两个文件里「橙」是不同色值,就是 bug。 ### 状态色的完整取值 | 令牌 | OKLCH | RGB 三元组 | 说明 | |---|---|---|---| | `--accent-primary` | `oklch(0.805 0.148 188)` | `0, 221, 209` | 霓虹青 | | `--accent-hover` | `oklch(0.870 0.150 188)` | `16, 243, 230` | 悬停态 | | `--accent-muted` | `oklch(0.520 0.060 188)` | — | 弱化强调 | | `--success` | `oklch(0.860 0.190 128)` | `173, 231, 78` | 荧光绿 | | `--warning` | `oklch(0.780 0.155 68)` | `247, 162, 50` | 琥珀橙 | | `--error` | `oklch(0.665 0.185 22)` | `240, 90, 94` | 红 | | `--accent-info` | `oklch(0.700 0.110 245)` | `95, 165, 222` | 蓝 | | `--kanban-review` | `oklch(0.722 0.177 305.5)` | `192, 132, 252` | 看板 `review` 列分类色(**分类色,非语义色**) | > `--kanban-review` 是唯一的分类色令牌:看板 9 个列状态装不进三色语义系统, > 按「流程阶段」维度分色。它仍然走令牌体系,所以 `rgba(var(--kanban-review-rgb), α)` > 能拼出弱底色。 **半透明用 RGB 三元组拼 alpha**,因为 OKLCH 无法直接拆 alpha: ```scss color: rgba(var(--accent-primary-rgb), 0.55); box-shadow: 0 0 12px rgba(var(--success-rgb), 0.22); ``` 每个需要拼 alpha 的令牌都有配套的 `--*-rgb`。要新增半透明语义色,**必须同时加 RGB 三元组**。 ### 语义底与语义线 | 令牌 | 值 | 用途 | |---|---|---| | `--accent-soft` | `oklch(0.805 0.148 188 / 0.13)` | 强调色弱底 | | `--success-soft` / `--success-line` | `… / 0.12` / `… / 0.40` | 绿色弱底 / 绿边框 | | `--warning-soft` / `--warning-line` | `… / 0.12` / `… / 0.42` | 橙色弱底 / 橙边框 | | `--error-soft` / `--error-line` | `… / 0.11` / `… / 0.42` | 红色弱底 / 红边框 | --- ## 2. 背景与描边 背景由深到浅四层,**越靠上层越亮**(向上浮): | 角色 | 令牌 | OKLCH | 用途 | |---|---|---|---| | 页面底 | `--bg-primary` | `oklch(0.155 0.012 250)` | 页面底色 | | 次级底 | `--bg-secondary` | `oklch(0.130 0.012 250)` | 卡片 / 面板(比页面更沉) | | 侧栏 | `--bg-sidebar` | `oklch(0.118 0.013 251)` | 比卡片更沉 | | 容器 | `--bg-card` | `oklch(0.185 0.014 248)` | 容器 / 输入区(比页面更亮) | | 容器悬停 | `--bg-card-hover` | `oklch(0.225 0.016 246)` | | | 输入底 | `--bg-input` | `oklch(0.105 0.012 252)` | 比卡片更沉,形成凹陷感 | | 代码底 | `--code-bg` | `oklch(0.115 0.014 250)` | | 描边发丝线分两档:`--border-color`(`oklch(0.305 0.018 242)`)、`--border-light`(`oklch(0.255 0.016 244)`)。 **硬编码 hex 是 bug**,除非是图表色板(见 §7)。 --- ## 3. 文字 | 角色 | 令牌 | OKLCH | 用途 | |---|---|---|---| | 正文 | `--text-primary` | `oklch(0.945 0.006 200)` | | | 次要 | `--text-secondary` | `oklch(0.860 0.008 210)` | | | 弱化 | `--text-muted` | `oklch(0.705 0.014 230)` | | | 极弱 | `--text-faint` | `oklch(0.520 0.014 236)` | 禁用态 / 极次要信息 | | 强调色上文字 | `--text-on-accent` | `oklch(0.160 0.030 200)` | 深色,保证对比 | | 浮层上文字 | `--text-on-overlay` | `oklch(0.945 0.006 200)` | | 反色场景**一律用 `--text-on-accent`**,不要硬编码白/黑。 --- ## 4. 字体栈 ```scss // UI(系统中英混排) $font-ui: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Segoe UI', Roboto, sans-serif; // 代码 / 命令 / 版本号 $font-code: ui-monospace, 'JetBrains Mono', 'SF Mono', Menlo, 'Cascadia Code', monospace; // 终端(xterm 在 JS 里读这个,所以必须是 CSS 自定义属性) --font-mono: ui-monospace, 'JetBrains Mono', 'JetBrainsMono Nerd Font', 'Nerd Font', 'SF Mono', Menlo, 'Cascadia Code', 'MesloLGS NF', 'Hack Nerd Font', monospace; ``` - **Nerd Font 回退段不能删**:默认 shell 提示符会输出私有区字形(分支、箭头、分隔符,如 `U+E0A0`–`U+E0D4`),缺了这些字形会整片显示为豆腐块。 - `$font-display` 复用 `$font-code` —— 等宽是 CYBER 的核心排版特征。 - 等宽字体只用于:代码块、命令、版本号、命令行提示、标识与数据。 --- ## 5. 圆角 / 间距 / 字号阶梯 **圆角**(`$radius-*`)—— 发丝线 + 小圆角,**硬边而非圆润**: | token | 值 | 用途 | |---|---|---| | `$radius-sm` | 6px | 按钮、输入框、小卡片、tag(与 Arco `borderRadius: 6` 对齐) | | `$radius-md` / `$radius-lg` | 8px | 面板、弹窗、大卡片 | **间距**:4px 基数,常用档 `4 / 8 / 12 / 16 / 24 / 32`。组件内边距多用 12/16,区块间 24/32。 **布局尺寸**: | token | 值 | |---|---| | `$sidebar-width` | 240px | | `$sidebar-collapsed-width` | 64px | | `$header-height` | 60px | | `$breakpoint-mobile` | 768px | **字号阶梯**: | px | 用途 | |---|---| | 10 | 分组标签(uppercase + letter-spacing) | | 11–12 | 辅助信息、meta、tag | | 13 | 次要正文、列表项、错误提示 | | 14 | 正文默认(`body` 基准) | | 15 | 输入框文字(`$font-code` 场景) | | 16 | 小标题 | | 26 | 登录页等展示型大标题 | **过渡**:`$transition-fast: 0.15s ease` / `$transition-normal: 0.25s ease`。 --- ## 6. 霓虹效果层 `styles/global.scss` 提供的 mixin,是 CYBER 辨识度的来源: | mixin / 动画 | 用途 | |---|---| | `neon-tube` | 霓虹灯管:主按钮、激活态边框 | | `neon-sheen` | 表面扫光 | | `neon-pulse` | 呼吸微光 | | `@keyframes live-ring` | **「机器在动」的唯一指示形态**(单色绿,不轮播) | | `@keyframes led-breathe` | 状态灯呼吸(仅连通时) | ### 「机器在动」只长一个样 流式输出、语音播放、工具执行中、连接健康 —— 全部用 `live-ring`(荧光绿单色呼吸)。 **不要做多色轮播**:同屏内四种语义色同时在闪,等于四种颜色都不再表达含义。 这条在 P0 修 `rainbow-glow` 时被违反过,回归护栏在 `tests/e2e/chat-cyber-semantics.spec.ts`。 ### 状态灯 `.cy-led` 7px 圆点 + 发光。连通=绿(呼吸),断开=红(**静止**)。 **一个死掉的连接不该看起来还在动** —— 断开态不呼吸。 ### 发光条用 inset box-shadow 左侧/顶部发光条用 `box-shadow: inset 2px 0 0 `,**不要用 `border-left`** —— border 会把内容宽度挤动 2px,列表行高对不齐。 --- ## 7. 图表色板(唯一允许硬编码颜色的地方) 图表/数据可视化的分类色按数据维度取色,**不走语义三色** —— 它们表达的是 「这是第几个类别」,不是「状态」。 规则: - 同一张图内的分类色必须**互相可辨**(色相间隔足够) - 图表色板集中定义在图表组件内,不散落 - 图表**状态**(成功/失败/警告)仍走语义令牌 - 图表色**不得**与语义色撞色到读者会误读为状态的程度 --- ## 8. 组件使用约定 - **消息 / 对话框 / 通知**:只准调 `composables/useMessage|useDialog|useNotification` 适配层(签名冻结),**不得**在页面里直接 import Arco 的 `Message` / `Modal` / `Notification`。 - **Modal vs Drawer**:短内容确认、纯信息展示用 `a-modal` (信息型弹窗 `:footer="false"`);需要表单编辑、长内容滚动的场景用 `a-drawer`。 - **按钮**:语义 `type` + `status` 组合(warning 按钮 = `type="primary" status="warning"`); 占满宽用 `long`;尺寸 `size="mini|small|medium|large"`;提交按钮 `html-type="submit"`, 加载态用 `:loading`。 - **输入框**:`a-input` 关闭了 attrs 透传,`autofocus` 无效 —— 需要自动聚焦时 `ref` + `onMounted` 调 `focus()`;密码框 `type="password"`。 - **表格空态**:用 `a-empty`,不手写占位文案。 - **loading**:按钮级用 `a-button :loading`,区域级用 `a-spin :loading`, 全局任务进度用 `useMessage().loading(...)`(用完 close/destroy)。 - **表单**:`a-form` + `a-form-item`,校验规则 API 与 naive 不同,逐例适配。 - **tag**:`color="arcoblue"` 等内建色,`:bordered="false"` 直接支持。 ### Arco 组件的暗色 项目**不挂 ConfigProvider**。Arco 的实际外观由 `styles/global.scss` 里的 `--color-*` / `--arcoblue-*` / `--green-*` / `--orange-*` 变量覆盖承载。 改主色时要同时改两处:`global.scss` 的 token bridge 和 `styles/arco-theme.ts` (后者是意图说明 + 测试锚点)。 **Arco 的 RGB 三元组变量不能当颜色用**: `--arcoblue-6` 的值是 `22,93,255` 而不是 `#165dff`。 写成 `color: var(--arcoblue-6)` 会被静默丢弃并回落到 Arco 默认色。 正确写法:`color: rgba(var(--arcoblue-6), 1)`。 --- ## 9. 装饰位面的边界 装饰位面(ArkScope vendor 组件等纯视觉层)的判定标准: > **不得承载信息、不得承载交互、不得影响可读性。** | 允许 | 禁止 | |---|---| | 登录页标题区装饰位面 | 聊天页(消息区 / 输入区 / 侧边栏) | | Journey 图谱的背景层 | 任何表单页(Skills / Profiles / Settings / Models) | | 首屏 loading / 空状态 | 任何表格页(Usage / Logs / Jobs / Kanban) | | | 任何点击交互密集区域 | **`pointer-events: none` 是硬要求**,装饰层绝不能吃掉点击 (样板:`LoginView.vue` 的 `.login-decor`,外加 `aria-hidden="true"`)。 vendor 素材边界:`vendor/arkscore/` 下**零图片/音视频素材**, 由 `tests/client/arkscope-vendor.test.ts` 机械化卡住;Provenance 见 `vendor/arkscore/PROVENANCE.md`。 --- ## 10. 四条不可破的规则 1. **不引入第三个 UI 库**——只允许 Arco Design Vue(+ 现有图标/工具链)。 2. **消息气泡内部不用 Arco 组件**(性能红线,见 Phase 006)。 3. **装饰层一律 `pointer-events: none`**,不承载信息与交互。 4. **颜色只走令牌**——语义色走三色系统,图表色走 §7 色板,硬编码 hex 一律视为待修。