Sim 开源仓库 EMCN 设计系统审查指南:组件、Token 与规范对齐实践
【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim
本篇技术指南面向在 Sim 开源仓库中编写或审查 UI 代码的开发者与 AI Agent,系统讲解emcn设计系统的组件、设计令牌(design tokens)、使用规范与反模式清单。读完本文,你将掌握如何通过.agents/skills/emcn-design-review/SKILL.md定义的工作流,对指定代码范围进行设计系统一致性审查,并能基于packages/emcn源码与apps/sim/app/_styles/globals.css中的真实 Token 定义,写出符合规范的界面代码。
背景:Sim 仓库中的 emcn 设计系统
Sim 是一个用于构建、部署与监控 AI Agent 和工作流的协作平台。在其 monorepo 中,packages/emcn是一个自研组件库,构建于 Radix UI 原语(primitives)之上,采用 CVA(class-variance-authority)管理变体,并以 CSS 变量作为设计令牌。仓库规则要求:所有 UI 必须使用 emcn 组件与 Token,禁止直接使用原生元素或硬编码颜色。
emcn 的组件覆盖面很广,从packages/emcn/src/components/index.ts的导出可见:基础输入类(ChipInput/ChipTextarea/ChipSelect/ChipDropdown/ChipCombobox/ChipDatePicker/ChipTimePicker/ChipEmailsInput/ChipSwitch/ChipTag)、模态类(ChipModal系列)、数据展示类(Table*、Badge、Avatar、Skeleton)、反馈类(Toast、Banner、Tooltip、Popover)、以及Calendar、Code编辑器、Wizard等高级组件。所有组件、cn工具函数与 Token 均从@sim/emcn单一 barrel 导出。
// packages/emcn/src/index.ts 的部分导出结构 export * from './components' export { Calendar, type CalendarProps } from './components/calendar/calendar' export { Table, TableBody, ..., TableRow } from './components/table/table' export { cn } from './lib/cn' export * from './icons'审查工作流:从范围到修复
emcn-design-reviewSkill 接受两个参数:
- scope:审查范围(默认是当前改动),例如
diff to main、PR #123、src/components/、whole codebase; - fix:是否直接应用修复(默认
true),设为false时只提出修改建议。
其标准执行步骤为:
- 阅读
packages/emcn/src/index.ts公共 barrel,了解可用组件、Calendar、Table*与图标;完整图标集见packages/emcn/src/icons/index.ts; - 阅读
apps/sim/app/_styles/globals.css获取 CSS 变量 Token; - 针对指定范围逐条对照下述规范进行分析;
- 若
fix=true则直接应用修复,否则仅提出建议。
导入规范:永远走 barrel
- 组件、
cn与 Token 一律从@sim/emcnbarrel 导入,禁止从组件子路径导入; - 图标从
@sim/emcn/icons导入。
这一约定背后有真实的历史教训。packages/emcn/src/index.ts的注释显示:Calendar、Code、Table同时存在于组件与图标两处。例如Table若被当作图标渲染,会画出一个空的w-full表格挤压兄弟元素(曾导致表格头部的 "T…" 闪烁问题)。因此 barrel 通过显式再导出把歧义解析为组件,而图标必须显式从@sim/emcn/icons获取。
设计令牌(Design Tokens):CSS 变量优先
规则核心:使用 CSS 变量模式text-[var(--text-primary)],绝不使用Tailwind 语义类(如text-muted-foreground)或硬编码颜色(如text-gray-500、#333)。Token 的实际取值定义在apps/sim/app/_styles/globals.css中,并区分浅色与深色两套主题。
文本色
| Token | 浅色值示例 | 用途 |
|---|---|---|
--text-primary | #1a1a1a | 主要文本 |
--text-secondary | #525252 | 次要文本 |
--text-tertiary | #5c5c5c | 三级文本 |
--text-muted | #7a7a7a | 弱化文本 |
--text-body | #434343 | 规范值(canonical value)文本 |
--text-icon | #5a5a5a | 图标着色 |
--text-placeholder | #8d8d8d | 占位符 |
--text-subtle | #8c8c8c | 极弱文本 |
--text-inverse | #ffffff | 反色文本 |
--text-error | #ef4444 | 错误/危险文本 |
(深色主题中这些变量会切换为#e6e6e6、#cccccc等对应值,--text-error保持#ef4444。)
表面(Surfaces)、边框与品牌色
- Surfaces:
--bg、--surface-1至--surface-7、--surface-hover、--surface-active。浅色下--surface-1为#fbfbfb(侧栏/面板)、--surface-2为#ffffff(块/卡片/模态)、--surface-4为#f5f5f5(按钮基底)、--surface-5为#f3f3f3(输入框、表单元素); - Borders:
--border。--border-1与--border-muted是解析到--border的历史遗留别名——审查时发现新代码使用它们应标记; - Brand/accent:
--brand-secondary(浅色#33b4ff)、--brand-accent(浅色#33c482)、--brand-accent-hover。
Z-Index 层级
globals.css 中注释明确了层级设计意图:Toast 栈是"环境级"的,位于页面内容之上但低于模态与瞬态弹出层,避免通知遮挡打开的模态或下拉菜单:
| Token | 值 | 用途 |
|---|---|---|
--z-dropdown | 100 | 下拉 |
--z-toast | 150 | Toast |
--z-modal | 200 | 模态 |
--z-popover | 300 | Popover |
--z-tooltip | 400 | Tooltip |
--z-takeover | 500 | 全屏接管页 |
--z-shell-gate | 600 | 桌面 Shell 门卫 |
阴影与徽章
- Shadows:
shadow-subtle(0 2px 4px 0 rgba(0,0,0,0.08))、shadow-medium、shadow-overlay、shadow-card(0 1px 3px rgba(0,0,0,0.04)),另有--shadow-ambient与--shadow-kbd等扩展; - Badges:
--badge-*语义族(success/error/gray/blue/purple/orange/amber/teal/cyan/pink,每族含-bg与-text),浅色示例:--badge-success-bg: #bbf7d0、--badge-error-text: #dc2626。
按钮(Buttons):意图到变体的映射
Skill 给出了动作类型到按钮变体的映射表,同时要求审查者阅读packages/emcn/src/components/button/button.tsx中的真实buttonVariants获取完整变体集。
| 动作 | 变体 |
|---|---|
| 工具栏、纯图标 | ghost |
| 创建、保存、提交 | primary |
| 取消、关闭 | default |
| 删除、移除 | destructive |
| 选中态 | active |
| 切换开关 | outline |
从源码看,buttonVariants实际暴露的变体比上表更丰富:除上述六个外还有3d(立体按压效果)、secondary(品牌色基底)、tertiary(accent 品牌色)、subtle、ghost-secondary、quiet;尺寸有sm(11px 文本)、md(12px 文本,默认)与icon(20px 方形、rounded-sm、p-0)。其中icon尺寸配合quiet/ghost变体时,会通过 compound variant 自动使用--text-icon-muted着色——因为孤立的字形是图标内容而非文本。一个典型用法:
<Button variant='quiet' size='icon' aria-label='Dismiss'> <X className='size-[16px]' /> </Button>删除/移除确认
删除操作必须走ChipModal且size='sm',标题为 "Delete/Remove {ItemType}",确认按钮用 destructive 变体,取消按钮用普通样式,并遵循.claude/rules/emcn-components.md中描述的 chip footer 布局。对于不可逆操作的危险提示,使用text-[var(--text-error)]着色。这与packages/emcn/src/components/chip-modal/chip-modal.tsx(约 1700 行)中ChipConfirmModal、ChipModalFooter等导出能力对应——该系列还提供ChipModalBody、ChipModalField、ChipModalError、ChipModalTabs等结构化构建块。
Toast:统一走 emcn API
任何通知都必须使用toast.success()、toast.error()或toast()(来自@sim/emcn),严禁自建通知 UI。packages/emcn/src/components/toast/toast.tsx中的createToastFn提供了统一工厂,其文档示例展示了 API 形态:
toast.error('Upload failed', { description: 'Network timed out' }) toast.success('Saved', { action: { label: 'View', onClick: () => router.push('/x') } })Badges:语义化的状态颜色
Badge组件(packages/emcn/src/components/badge/badge.tsx)由cva定义两类变体:带边框的default/outline/type,以及无边框的状态色变体(green/red/gray/blue/blue-secondary/purple/orange/amber/teal/cyan/pink/gray-secondary)。状态色变体支持dot属性渲染圆点指示器(尺寸随sm/md/lg变化:5px/6px/6px),也支持icon前置图标。
Skill 规定的语义映射为:
| 场景 | 变体 |
|---|---|
| 错误/失败 | red |
| 元数据/角色 | gray-secondary |
| 类型注解 | type |
| 成功/激活 | green |
| 中性 | gray |
| 处理中 | amber |
| 暂停 | orange |
| 信息 | blue |
状态指示一律使用dot属性。
Icons:尺寸与着色约定
- 默认尺寸:
size-[14px]; - 颜色:
text-[var(--text-icon)]; - 尺寸优先级:14px > 16px > 12px > 20px;
- 使用
size-*简写,h-[Npx] w-[Npx]与h-N w-N成对写法属于待重构目标,审查时应标记。
完整图标集从packages/emcn/src/icons/index.ts查看,包括Arrow*、Circle*、Chevron*、Trash、Zap、Wand、Webhook、Workflow、TerminalWindow、BrainCircuit以及类型标注图标(TypeBoolean/TypeNumber/TypeText/TypeJson/TypeCurrency/TypeTtl)等 150+ 个图标。
反模式清单(Anti-patterns)
审查时以下模式应被标记并修复:
- 使用原生
<button>/<input>,或遗留的Input/Textarea/Modal,而非规范的 chip 组件(ChipInput/ChipTextarea/ChipModal); - 在
ChipModalBody内手写字段行,而非使用ChipModalField; - 硬编码颜色(
text-gray-*、#hex、rgb()); - Tailwind 语义类(如
text-muted-foreground)替代 CSS 变量; - 用模板字符串拼 className 而非
cn(); - 对颜色/静态值使用内联样式(动态值允许);
- 从 emcn 子路径导入而非 barrel;
- 使用任意 z-index 而非 Token;
- 动作类型与按钮变体不匹配。
其中cn()工具本身在packages/emcn/src/lib/cn.ts中实现:它组合clsx与tailwind-merge(v3,匹配应用的 Tailwind v4 工具面),并通过扩展font-size类组(micro/caption/small/md)确保 Sim 自定义的文本尺度键能正确参与冲突消解,实现"后者胜出"。
小结
emcn-design-review是一份可执行的、以规范驱动的 UI 审查清单:它以 barrel 导入约束来源、以 CSS 变量 Token 约束样式、以意图-变体映射约束按钮与徽章语义、以统一 API 约束通知行为,并以反模式清单兜底。在实际开发中,你可以把本文的 Token 表、变体映射与反模式清单直接用作 Code Review 的检查项,或作为 Agent 生成 UI 代码时的约束上下文,从而让整个apps/sim的界面在视觉、可访问性与可维护性上保持一致。
提示:审查与修复仅针对源码工作区内的代码文件;仓库为只读,任何修改都应遵循各自的代码库协作流程。
【免费下载链接】simSim is the collaborative workspace to build, deploy, and monitor AI agents and workflows. Used by 100,000+ builders.项目地址: https://gitcode.com/GitHub_Trending/sim16/sim
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考