设计系统(wiki + dashboard 共用)

本质与导读

「不考虑工作量,最统一做法」—— 用户 2026-05-29 决策。本文是设计系统的契约

  • 上手指南。改样式前先看这里。

抓硬约束

  • 格式跟内容独立:markdown 内容(wiki/pages/*.md) 跟 CSS 样式完全解耦, 写新页 / 写 prose 不需要懂 design system
  • wiki + dashboard 双 app 共用:跨 app 视觉语言一致(同色 / 同卡 / 同 chip)
  • 改一处全站同步:token 只在 _tokens.css 改;primitive 只在 _atoms.css 改; 组件只在 apps/web/components/atoms/*
  • 便于扩展 / 移植:加新 app(如 mobile native / 第二 dashboard)只需 import 同 3 个文件 + 写自己 layout

因果分析:为什么 3 层

文件改什么谁能看到
Layer 1 Tokenapps/web/styles/_tokens.css颜色码 / 字号 / 阴影 / 字体全站全部
Layer 2 Primitiveapps/web/styles/_atoms.css卡形 / chip 形 / 按钮态 / callout 变体任何引用 CSS 类的位置
Layer 3 App-specificapps/web/styles/globals.css(wiki),apps/invest-dashboard/src/dashboard.css(dashboard)业务 specific(wiki prose, dashboard cc-* 容器)各 app 自己
Layer 4 React Atomapps/web/components/atoms/* (待建)<Card> <Chip> <Button> JSXTSX 引用方

单一职责:每一层只管它该管的。改色去 1,改卡形去 2,改业务排版去 3,改组件 prop 去 4。 禁止逆向:Layer 3 不能 hardcode #f2e9dc(应 rgb(var(--c-page-bg)));Layer 2 不能加 body / :where(h1) 这种业务选择器。

解决方案:目录 + 命名

Layer 1 —— apps/web/styles/_tokens.css

:root {
  /* 颜色 RGB triple(无 # 让 Tailwind /alpha 复用)*/
  --c-page-bg:   242 233 220;   /* warm cream 全站背景 */
  --c-paper:     248 243 236;   /* warm ivory 卡片底 */
  --c-ink:        58  54  50;   /* charcoal 主文字 */
  --c-accent:    194 170 147;   /* softened sand 品牌色 */
  --c-danger:   175  90  80;
  --c-success:  120 150 115;
  /* ... */
  /* 字号 / radius / shadow / 字体 */
  --r-lg: 12px; --r-xl: 16px; --r-pill: 9999px;
  --fs-body: 14px; --fs-label-xs: 11px;
  --font-ui: "Noto Sans SC", ...;
}
html[data-theme="dark"] { /* 同 token 覆深色值 */ }

Layer 2 —— apps/web/styles/_atoms.css

/* 表面 */
.surface-card     { background, border, radius, padding }
.surface-card--lg { ... bigger ... }
.surface-card--nested { ... 卡中卡 ... }
.surface-flat     { 透明 wrapper }
.surface-chip     { 圆形 chip / pill }

/* 文字 */
.text-primary .text-muted .text-accent
.text-success .text-danger .text-warning .text-info

/* 状态色背景(透明叠加)*/
.bg-success .bg-danger .bg-warning .bg-info

/* 按钮 */
.btn-ghost .btn-primary

/* Callout 变体(BEM)*/
.callout
  .callout--risk
  .callout--checklist
  .callout--info
  .callout--warning
  .callout--success
  .callout--debugtree

Layer 3 —— app-specific

Wiki.wiki-prose(typography)+ 老 .wiki-article-card .wiki-panel-card 做 layout container,视觉为 transparent(2026-05-29 扁平化)。callouts 用 .callout + .callout--<variant> 而非自己定义。

Dashboard.invest-cc 做容器 + .cc-panel / .cc-kpi / .cc-tag 做卡。 未来:cc-* 类逐步迁移到引用 surface-card / surface-chip(本 session 不动, 留迁移期)。

Layer 4 —— React atom 组件(待建)

apps/web/components/atoms/ 计划:

  • <Card variant?="lg|nested|flat">
  • <Chip variant?="success|danger|warning|muted">
  • <Button variant="ghost|primary" />
  • <Callout variant="risk|checklist|info|warning|success">

JSX 用法:

import Card from '@shared/atoms/Card';
<Card variant="lg">…</Card>

Import 顺序(强制)

任何 app 入口 CSS 文件必须这个顺序 import:

/* 1. Token 必须最先 — 其他用 var() 引 */
@import "./_tokens.css";

/* 2. Primitive 用 token 定义 — 必须在业务样式前 */
@import "./_atoms.css";

/* 3. 业务样式(可 override primitive,但**只 override 不 redefine**)*/
.invest-cc { /* dashboard 容器 */ }
.wiki-prose { /* wiki typography */ }

主题(浅 / 深 / 霓虹)

  • 真源:_tokens.csshtml[data-theme="light"|"dark"|"neon"] block
  • 切换:document.documentElement.dataset.theme = '...',localStorage 持久化
  • 状态栏:wiki ThemeToggle + dashboard DashboardShell.applyTheme 都用同套 STATUS_BAR map(light: #f2e9dc, dark: #1f1d1b, neon: #0a1124),通过 <meta theme-color> 同步 iOS / Android 顶栏。详见 memory [feedback_theme_color_dynamic_only]

防漂移规则(REVIEW 时必查)

  1. 新颜色不 hardcode —— color: #98806a ❌,color: rgb(var(--c-accent-dark))
  2. 新卡用 .surface-card —— 不要再写 border: 1px solid; border-radius: 12px; 一遍
  3. 跨 app 样式必上 Layer 2 —— 只有 1 个 app 用的可留 Layer 3
  4. token 改了必跑 python3 scripts/automation/system_content_health.py 确认 0 issue
  5. lint python3 scripts/lint/lint_pages.py 跨色 chip / wiki-article-card 不要在 prose 里再嵌套
  6. 字号只用 8 级 grid —— 12/14/16/18/20/24/28/32(单源 _tokens.css)。新写 font-size 必落网格、 优先挂 --fs-*;禁碎值(11.5/13.5/17/21)、禁 off-grid calc(+1px)。 6b. hand-SVG 字号只用 4 级网格 —— 图内文字独立坐标系、不走 CSS token,收敛到 18=图标题 / 16=框内 / 11=框外 / 10=脚注·单位(2026-06-01 上抬,原 16/13/11/10)。 两种写法都算:font-size: 内联/属性 + <style>font:Npx 简写;lint_pages.pysvg-font-scale(两正则)已强制。禁 8/9/12/14/15/17/22 碎值。 升号迁移经验(2026-06-01 全量 826 张 13→16/16→18):hand-SVG 框普遍有 padding 余量, +2/+3px 升号在 pilot+启发式榜首 7 张均无破版。mermaid 已于 2026-06-01 全部转 hand-SVG,wiki 不再有 mermaid 图;新图一律 hand-SVG。 6c. wiki md 正文阶梯 4 级 24/20/18/14(2026-05-31)—— .prose.wiki-prose 渲染域: H1=24 / H2=20 / H3·H4=18 / 正文=18(--fs-wiki-body,p·li·blockquote)/ code·meta=14。 原则:H 比正文大的保持大(H1·H2),≤正文的 H 就用正文字号(H3/H4=18),不发明碎档; H3/H4 与正文同号靠加粗+颜色分层。HARD:正文 ≤ 每一级标题、绝不大于标题(旧 bug 正文 20 > H2 18 倒挂已修)。 正文用 wiki-prose 专属 override,不动共享 --fs-prose-body(srs 单独 .prose 仍正文 20 / H1 28,自成正常阶梯)。
  7. 手机端正文/列表零缩进 —— ≤768px 嵌套列表 padding 归零,层级靠 marker(· → ◦ → ▪)/ 颜色 / 标签, 不靠缩进推右(deep page 深嵌套手机不溢出);桌面可保留缩进。
  8. @media 政策 —— 连续属性(字号/间距/列宽)一律流式:clamp() / auto-fit minmax() / 横滚, 禁断点切换致拉伸突变;@media 只留真·布局模式切换(多列→单列 / 显隐 / 手机缩进)。
  9. 全局 token/reset 禁写 globals.css —— dashboard 不 import globals.css;凡全局 token / <button> reset / 胶囊 chrome 放 _tokens.css(L1)/ _atoms.css(L2)。dashboard 组件 CSS 单源 _dashboard.css(SPA dashboard.css + wiki globals.css 各 @import 一次)。

跨 app port 流程(产品角度)

未来要起第三个 app(比如手机原生 React Native / 公开版独立站):

  1. import _tokens.css + _atoms.css(只动 path)
  2. 视觉立刻 90% 对齐(token + primitive 全套)
  3. App-specific 排版只写 Layer 3 几十行
  4. React Native 不支持 CSS → 用 apps/web/styles/_tokens.json(未来从 _tokens.css 导出)
  5. 加新 page 只用 Layer 4 atom 组件,不写 CSS

历史(怎么走到这步)

时间事件
2026-05-28 早wiki / dashboard 各持一份 c-* token 镜像,改一处不同步
2026-05-28 中_tokens.css 单一真源,两边 @import
2026-05-29 早wiki 卡片 mobile 仍有 2 层(article-card 包 prose),dashboard 1 层
2026-05-29 中.wiki-article-card 全屏扁平化(原 lg+ 已扁平),surface-card-sm/lg 全 opacity 对齐 dashboard cc-panel
2026-05-29 晚用户决策「最统一做法」→ 抽 _atoms.css Layer 2 primitive 层,建本 docs
未来Layer 4 React atom 组件(<Card> <Chip> 等),旧 wiki/cc-* 类逐步迁移

链接