6.4 KiB
样式与设计 Token 规范
Anthropic 风(warm editorial)设计体系:色板、字体、圆角与反模式清单。代码事实源:
src/theme/tokens.ts(JS 侧唯一来源)与src/assets/main.css(Tailwind@theme)。
基本规则
- 设计 token(色板、圆角、字体、间距)唯一来源是全局 CSS 变量(Tailwind
@theme),Naive UIthemeOverrides从同一份取值;组件内禁止散落硬编码 hex。 - Tailwind 工具类只用于布局、间距、排版;组件视觉(按钮/输入/表格)交给组件库主题,不用工具类二次描皮。
- 间距遵循 4px 网格;圆角、阴影、配色遵守下方 token 与反模式清单。
风格特征
- 暖纸色底:不用纯白,用象牙色
#FAF9F5做主背景,靠 1px 细边框(hairline)和一层灰度差分层,几乎不用投影。 - 单一强调色:赤陶橙(terracotta)只出现在主按钮、激活态、品牌标记上,页面 95% 面积是黑白灰米。
- 小圆角、扁平:圆角 4–6px 量级;无渐变、无玻璃拟态、无 3D 浮雕、无大面积色块光晕。
- 排版即装饰:层级靠字号/字重/留白拉开;图标用细线性风格(lucide/tabler),不用 emoji 和彩色立体图标。
- 克制的动效:过渡 150–250ms、只做透明度/位移,无弹跳和视差。
官方色值(Anthropic 官方 brand-guidelines)
主色:
| Token | 色值 | 用途 |
|---|---|---|
| Dark | #141413 |
主文本、深色背景 |
| Light | #FAF9F5 |
浅色背景、深底上的文本 |
| Mid Gray | #B0AEA5 |
次要元素、占位、禁用 |
| Light Gray | #E8E6DC |
细微背景、分隔线、边框 |
强调色:
| Token | 色值 | 用途 |
|---|---|---|
| Orange | #D97757 |
主强调(按钮、激活、链接 hover) |
| Blue | #6A9BCC |
次强调(信息、链接) |
| Green | #788C5D |
三级强调(成功) |
品牌橙近似变体(社区考证):#C15F3C(Crail,更深)与 #CC785C(Book Cloth),可用作橙色的 hover/active 深档。
管理面板适配 token
官方色板缺管理面板必需的语义色,按同色温补齐:
| 语义 | 建议值 | 说明 |
|---|---|---|
| 背景主 | #FAF9F5 |
页面底 |
| 背景面 | #F0EEE6 |
侧栏、区块底(Light 与 Light Gray 之间) |
| 卡片 | #FFFFFF |
白卡浮在暖底上,配 1px #E8E6DC 边框 |
| 文本主/次 | #141413 / #6B6A63 |
次级文本由 Dark 与 Mid Gray 插值 |
| 主强调 | #D97757,hover #C15F3C |
|
| 成功 / 信息 | #788C5D / #6A9BCC |
官方绿 / 蓝 |
| 警告 | #B8860B 一档的暖赭 |
与暖色调和谐,避免高饱和黄 |
| 危险 | #BF4D43 一档的砖红 |
避免纯红 #FF0000 系 |
| 暗色模式 | 底 #141413、面 #1F1E1D、边框 #33322E |
文本反转用 Light |
| 圆角 | 按钮/输入 4px,卡片 6px,最大不超过 8px | 拒绝大圆角 |
| 阴影 | 无或 0 1px 2px rgba(20,20,19,.05) |
分层靠边框 |
字体
- 官方品牌字体 Styrene(无衬线)+ Tiempos(衬线)为商业授权,不可直接使用;官方开源替代:标题 Poppins、正文 Lora。
- 本项目:UI 与数据全用无衬线(
Inter或Poppins,中文回退PingFang SC/Noto Sans SC);OCID、IP、日志等用等宽(JetBrains Mono/ui-monospace);衬线(Lora)只做登录页/大标题的编辑风点缀,可选。
反模式清单(设计稿与代码评审逐条对照)
大圆角(>8px 的按钮/卡片)、任何渐变(含品牌色渐变文字)、玻璃拟态/毛玻璃、霓虹光晕、彩色大投影、emoji 当图标、深紫深蓝"AI 感"配色、满屏色块卡片、弹跳动效。
Common Mistake: NModal 宽度不能用 Tailwind class
Symptom:<NModal preset="card" class="w-[480px]"> 弹窗实际渲染为全宽。
Cause:naive-ui 的样式为运行时注入(CSSinJS,<style> 追加在 head 尾部),其 .n-card{width:100%} 与 Tailwind utilities 同特异性但后加载,覆盖 w-[480px]。
Fix / Prevention:弹窗宽度一律用内联 style(优先级最高),与 FormModal.vue 同法:
<!-- Bad:被 naive-ui 运行时样式覆盖 -->
<NModal preset="card" class="w-[480px] max-w-[92vw]">
<!-- Good -->
<NModal preset="card" :style="{ width: '480px', maxWidth: 'calc(100vw - 24px)' }">
Common Mistake:编辑 SFC 时残留 defineProps<...>()() 双括号
Symptom:运行时 ReferenceError: defineProps is not defined(setup 内),但 vue-tsc、vite build 全绿——编译期完全静默。
Cause:对 defineProps<{...}>() 做字符串替换时旧串漏了尾部 (),替换后残留成 ()()。@vue/compiler-sfc 只识别「宏调用语句」,对「调用宏返回值」的表达式不做宏转换,把 defineProps 原样保留成运行时标识符。
Prevention:替换 defineProps/defineEmits 行必须包含完整语句(含结尾 ());宏相关改动后,不能只信 build 全绿,须实际打开一次使用该组件的页面(详情弹窗等惰性挂载组件尤其容易漏)。
Common Mistake:scoped style 中 :global(...) 后接后代选择器会丢失后代
Symptom::global(html.dark) .totp-qr { ... } 编译产物变成 html.dark { ... }——.totp-qr 整段丢失,规则错误落到 html 元素上(background/padding 直接作用于全页),目标元素完全不匹配。build 全绿、无告警。
Cause:@vue/compiler-sfc 的 scoped 转换对「:global() 后跟普通后代选择器」的组合处理不可靠,后代部分被丢弃而非追加 scope id。
Fix / Prevention:完整选择器整体放进 :global():
/* Bad:编译后 .totp-qr 丢失,规则落到 html.dark 本身 */
:global(html.dark) .totp-qr { background: #f5f4ed !important; }
/* Good:原样输出 html.dark .totp-qr(自命名 class 需保证全局唯一) */
:global(html.dark .totp-qr) { background: #f5f4ed !important; }
验证方法:build 后 grep -o 'html\.dark[^}]*}' dist/assets/<view>-*.css 核对编译产物选择器,或线上 DevTools 看 computed style 是否命中。
组件速查:NQrCode 去白底
NQrCode 自带 inline background-color:#FFF; padding:12px(props 可控)。两主题都不垫任何色块的方案::padding="0" background-color="transparent" + :color="app.dark ? '#f5f4ed' : '#141413'"——暗色下反色渲染(浅模块透明底),主流扫码器(iOS 相机 / Google Lens / 新版 ML Kit)可识别,扫不动时弹窗内的手动密钥是兜底。color 是响应式 prop,切主题自动重绘。(历史方案「html.dark 下垫 #f5f4ed 浅底」因用户不接受色块框已废弃。)