ui-design-system-builder

当使用者说"帮我做个界面"、"这个页面太丑"、"看着像模板/像 AI 做的"、"给这个项目定个视觉风格"、"配色和字体你来定"、"把这几个页面统一一下"时使用。先从项目本身抠出一个具体意象,据此定出一版完整的 CSS 变量与排版尺度(不是"现代简洁"这种废话),再用这些 token 拼组件;每个非显然的取舍都在代码注释里写清理由、代价和怎么改回去。中文界面必须同时配 CJK 字体。不装 UI 框架

SKILL.md

GitHub

定一套界面的视觉语言 / UI Design System Builder

给一个项目定出它自己的视觉语言:一版 token、一套排版尺度、几个用 token 拼出来的组件。

界面之所以像模板,不是因为审美差,是因为每个决定都取了默认值 —— 默认的 Inter、默认的 8px 圆角、默认的 0 4px 12px rgba(0,0,0,.1)。 这个技能要求每个决定能说出为什么是这个项目该用的。

何时使用 / When to use

  • 项目要起前端,还没定视觉
  • 已有界面能用但"像模板 / 像 AI 随手做的"
  • 几个页面各写各的样式,想统一
  • 要换字体、换配色、加暗色模式这类会波及全站的改动

不适用于 / Not for:

  • 只改一个按钮的边距 —— 直接改,别起一套系统
  • 已经有设计稿或设计规范 —— 那是照着实现,不是重新定
  • 想直接套用 MaestrWave 那套现成的深色衬线视觉 —— 用 maestrwave-ui-system, 那边有可粘贴的 global.css 与组件层,不用从头推
  • 组件库选型、状态管理、性能优化 —— 那是别的问题

一条主线:意象 → token → 组件

倒过来做就会失败。先挑组件、边写边配色,最后拼出来的一定是一堆互不相干的局部最优。

1. 从项目里抠一个具体意象

读 README 的项目定位和领域名词,只读这些,找一个能画出来的意象。

弱 强
现代、简洁、专业 月光下的水面(音频波形应用)
科技感 老式示波器的荧光余晖
温暖友好 图书馆闭馆前那盏台灯

判据:这个意象能直接推出颜色和材质。"简洁"推不出任何具体值,"月光下的水面"能推出 暖炭黑基底 + 浅水蓝主色 + 低透明度的叠层波浪。

抠不出来就问一句,别自己编一个宏大的概念糊上去。

2. 直接写出一版完整 token

不要贴调色板让人挑。写出一份能直接粘进 global.css 的 :root, 包含背景层级、主色、状态色、文字色、描边色、字体栈,每一支都在注释里写为什么。

:root {
  --bg: #15130f;          /* 暖炭黑,不用纯黑:纯黑上任何颜色都发脏 */
  --surface: #211c15;
  --surface-2: #2c2519;   /* 层级靠明度递增,不靠阴影 —— 深色界面里阴影几乎看不见 */
  --accent: #7cb2e8;      /* 浅水蓝,呼应波形主题 */
  --wine: #b8453a;        /* 状态色(录制/危险),和蓝形成一冷一暖 */
  --ink: #f3eddd;         /* 暖白,不用 #fff:和暖底搭配才不割裂 */
  --line: rgba(243, 237, 221, 0.1);
}

要点:

  • 层级用明度台阶(surface / surface-2 / surface-3),深色界面里阴影不出力
  • 文字层级用 color-mix(in srgb, var(--ink) 55%, transparent),不另起一堆灰色 token
  • 状态色只留一支够用了,第二支状态色多半是没想清楚
  • token 是唯一真相。canvas / SVG 也要读 token(getComputedStyle 取回来), 不然换主题时这些地方会被漏掉

3. 定尺度对比,而不是定一堆字号

一套排版系统的性格来自极端之间的落差,不是来自中间有多少档。

display-1   clamp(40px, 6vw, 64px)  weight 600  line-height 0.98  letter-spacing -0.02em
display-2   clamp(24px, 3vw, 32px)
body        14px                    line-height 1.5
eyebrow     11px  letter-spacing 0.14em  uppercase  用主色
label       12px  透明度 55%

巨大的标题配 11px 的全大写小标签,这个对比本身就是设计。中间再加三档只会把它磨平。

把这套固化成一个 PageHeader 组件(eyebrow + 大标题 + 小号元信息), 每页都用它,不允许各页自己写标题样式。

4. 组件从 token 拼,样式分两层

  • 跨页复用的排版类(.display-1 .eyebrow .label .mono-chip .field-label) → styles/global.css,全局可用
  • 只属于某个页面/组件的布局 → 同目录的 X.module.css

判据很简单:第二个地方要用到了,就往 global 提。在页面里第二次复制粘贴同一段 标签样式时,就是该提的时候。

层级要换手法,不要同一手法调强度:一级导航用「描边 + 微亮底」,二级用「实心高亮」。 两级都用实心块的话,视线分不出主次,加大色差也救不回来。

5. 可用性底线,写完对一遍

  • @media (prefers-reduced-motion: reduce) 全局把动效降到 0.001ms
  • 装饰层 aria-hidden="true" + pointer-events: none
  • 焦点统一:border-color: var(--accent) + 3px 的半透明 ring,不要 outline: none 了事
  • :disabled 用 opacity: 0.4 + cursor: not-allowed
  • 数字(时间码、计数、图表刻度)加 font-variant-numeric: tabular-nums,否则会跳
  • 动效只用来表达状态:加载中的呼吸、按下的 scale(0.98)。纯装饰的动画一律不加

6. 每个非显然的取舍留一段注释

这一步是这套方法真正省时间的地方。注释写三件事:为什么这么选、代价是什么、想改回去改哪一行。

/*
  --font-mono 现在也是衬线体(按「UI 全部改 Serif」的要求)。代价是数字不再等宽,
  图表刻度、时间码这些地方会参差 —— DOM 里靠 font-variant-numeric: tabular-nums
  补救,canvas 里补救不了。
  觉得不合适时把这一行改回 "IBM Plex Mono", ui-monospace, monospace 即可,
  其余代码全都引用 token,不用动。
*/

半年后回来看,没有这段注释就只能重新试一遍。

反默认自查 / Anti-default check

写完对一遍。命中任何一条,说明那里取的是默认值,重挑:

  • 配色是纯黑 + 荧光绿 / 暖白 + 赤陶 / 紫蓝渐变
  • 字体是 Inter(或系统默认)配一个没指定的等宽
  • 全站一个圆角值
  • 阴影是 0 4px 12px rgba(0,0,0,0.1)
  • 主色是 #3b82f6 或它的近邻
  • 说不出任何一支颜色为什么是这个值

中文界面的字体 / CJK typography

界面以中文为主时,只换拉丁字体等于没换 —— 中文会掉到系统默认(macOS 上是黑体系), 一衬线一无衬线,界面立刻割裂。

  • 拉丁与 CJK 成对引入,字体栈里 CJK 排在拉丁之后(浏览器按字形覆盖逐字回退)
  • CJK 字重只引实际用到的两档。@fontsource 的中文包按 unicode-range 切成上百个分片, 每多一档,产物 CSS 就多几十 KB —— 而 CSS 阻塞渲染。中间档(500)交给浏览器回退到 400, 中文场合看不出差别
  • 中文没有小型大写和真正的斜体,text-transform: uppercase 那套只对拉丁标签有效

输出格式 / Output

一次给完,别分三轮问:

**视觉主张**:<一句话,含那个具体意象>

**Token**(粘进 `styles/global.css` 的 `:root`)

```css
<完整的一版,每支带注释>
```

**排版尺度**

<表格:类名 / 字号 / 字重 / 字距 / 用在哪>

**示范组件**:<挑一个最能体现这套语言的,给完整代码>

**我替你定了的**
- <取舍 1 + 代价 + 怎么改回去>
- <取舍 2>

边界 / Boundaries

  • 不装 UI 框架来跳过这一步。除非使用者明确要求,不要为了"快"引入 Tailwind / MUI / shadcn —— 那等于把视觉决定交给框架默认值,正好是这个技能要解决的问题。
  • 不改业务逻辑。只动样式、类名和纯展示组件;发现逻辑问题就说出来,别顺手改。
  • 不为了好看牺牲可读性。正文对比度不足、点击区小于 40px、焦点态看不见, 这些没有商量余地。
  • 不编造品牌资产。项目已有 logo、图标或既定色时,围着它们定,不要另起一套。
  • 不一次改完全站。先在一个页面上把这套语言坐实,跑起来看过,再铺开。
  • 不写"现代、简洁、优雅"这类词当作设计说明 —— 说不出具体值就是还没想清楚。

Token 控制 / Token discipline

  • 第一步只读 README 的项目定位段 + 现有 global.css + 一个代表性页面, 不通读全部组件。视觉语言是从项目定位推出来的,不是从组件清单里归纳出来的。
  • 需要 MaestrWave 的完整实例(token 表、层级手法、注释样例)时再读 references/maestrwave-example.md,别默认加载。
  • 改定后只贴改动的那几行,不重贴整份 CSS。

变更记录 / Changelog

版本 日期 变更 类型
0.1.1 2026-09-26 参考实例里 .field-label 的 ink 比例随 maestrwave-ui-system 由 48% 改为 52%(48% 在 11px 时对比度不到 AA 4.5:1),两边保持逐项一致 patch
0.1.0 2026-08-10 初始草稿,方法总结自 MaestrWave 前端 minor