ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Utopia 前端设计规范解读:六条设计规则、Token 体系与 style-guard 强制检查

Utopia 前端设计规范解读:六条设计规则、Token 体系与 style-guard 强制检查 后端前端人工智能RAG知识图谱知识管理搜索引擎【免费下载链接】utopiaWorlds first open-source enterprise world model.项目地址https://gitcode.com/gh_mirrors/ont/utopia点击查看免费下载Utopia 是一个开源的企业级世界模型world model项目其 Web 界面web/目录是一套基于 React 19 Tailwind CSS 4 shadcn/ui 的单页应用。本文以仓库内的 web/DESIGN.md 为骨架结合 web/src/styles.css、web/src/ui/index.tsx、web/scripts/style-guard.mjs 等源码完整解读这套界面背后的六条设计规则字号五档、间距六步、圆角四档、颜色令牌化、状态内聚于组件、面板即内容的槽。读完本文你将掌握这套设计体系的每一处数值、它在源码中的落地位置以及它如何通过 CI 守卫强制被执行。设计体系概览中性玻璃、两种主题、三层结构Utopia 的界面外观chrome遵循一个总原则中性玻璃neutral glass提供暗色与浅色两种主题。界面本体不引入任何色相hue文字使用 Geist 字体品牌字标wordmark使用 Manrope颜色只保留给两类用途——数据如图谱节点、实体色和少数语义状态成功、警告、危险、争议、紫色。组件层采用三层结构shadcn/uiRadix 基础、Nova preset位于 web/src/components/ui/由shadcn add生成是纯粹的供应层薄壳层web/src/ui/index.tsx封装 shadcn 组件定义本项目的组件词汇Button、Input、Dropdown、Status、SettingsCard等页面层只允许使用壳层组件与styles.css中定义的语义类不得直接书写原始控件、颜色值或状态伪类。设计语言本身记录在 web/src/styles.css共 1814 行而六条规则则被 web/scripts/style-guard.mjs 转成可执行的正则检查。正如 DESIGN.md 开头所言如果没有这些规则一个页面可能从十二种像素字号、十四种内距、任意一种灰色里各挑一个——守卫的作用就是让这种熵不再发生。规则一五档字号按名字引用而不是按像素字号只有五档且每档同时规定字号与行高名字字号 / 行高用途text-fine11 / 16元数据、芯片文字、表头、控件下方的提示text-small12 / 18次要文字、密集行、说明文字text-body14 / 22其余一切正文、控件、菜单text-title16 / 24区块与对话框标题聊天正文按此档阅读text-display20 / 28页面标题且仅限页面标题规则的核心约束是不使用text-xs/text-sm/text-[11px]这类任意值。如果某个元素在两个档位之间需要一个尺寸说明是元素选错了档位而不是刻度有问题。字重方面控件和标题用font-medium只有主按钮用font-semibold界面中不使用font-bold。数字在界面中一律用 Geist u-numtabular figures等宽数字不用等宽字体font-mono只留给密钥、ID、代码与 URL。在源码中的落地styles.css的theme块通过 CSS 变量定义了这五档web/src/styles.css并附有一段注释说明这套刻度经历过一次整体上移——从前是 11/12/13/15/20三档挤在 11–13px 之间1px 的字号差人眼几乎分辨不出层次名义上有五级实际靠颜色区分现在正文落到 14px最常用的那一档成为真正可读的正文。style-guard.mjs中的type-scale规则用正则\btext-(xs|sm|base|lg|\d*xl|\[[0-9.](px|rem)\])\b直接禁止这些写法unknown-text规则则动态地从styles.css中读取全部--text-*与--color-*令牌任何不在白名单内的text-词都会报错web/scripts/style-guard.mjs。规则二六档间距12 以上只用于版面间距步进为1 2 3 4 6 8对应 4、8、12、16、24、32 px内距padding、外边距margin和间距gap一视同仁不允许半档、不允许任意像素值。12 及以上属于版面layout而非节奏rhythm——浮动栏下方的空隙、页脚的呼吸空间这类场景允许使用。控件自带内距页面绝不直接给按钮或输入框设置 padding。页面级经验值页面左右边距gutter6或8两个相邻控件的间距2两组控件之间4两个区块之间6。style-guard.mjs的spacing规则禁止p-0.5、m-1.5、gap-2.5、space-x-3.5等半档写法以及p-[…]任意值12 以上由正则放行因为那是版面净空并且这条规则不适用于组件目录——组件的内部间距是组件自己的事web/scripts/style-guard.mjs。规则三四档圆角按角色命名圆角不叫rounded-sm/rounded-lg而是按角色命名rounded-cell芯片、表格单元格、kbd、小图标目标rounded-control按钮、输入框、下拉框、分段控件rounded-panel卡片、列表、对话框主体、代码块rounded-overlay菜单、popover、toast、悬浮停靠面板——任何悬浮在页面上方的东西rounded-full只用于真正的圆形头像、状态点、色板、图谱节点。命名的意义在于rounded-panel说明了这是一个面板就像text-ink-2说明了这个灰色是干什么用的——守卫能检查的是角色是否用错而不是数字是否正确。四个半径也不是各自拍脑袋定出来的而是从 shadcn 的单一--radius10px派生cell ×0.8、control ×1、overlay ×1、panel ×1.4。在源码中可以看到具体实现--radius-cell: calc(var(--radius) * 0.8); --radius-control: var(--radius); --radius-panel: calc(var(--radius) * 1.4); --radius-overlay: var(--radius);web/src/styles.css。这遵循 shadcn 自己的比例——卡片比悬浮在它上方的 popover 更圆。曾经这套值由手工挑选4、6、8、12结果与 shadcn 10px 的按钮并排时按钮比它所在的卡片还要圆。现在整个界面的圆润度只有一个旋钮改--radius一处控件与面板一起动。这个比例还带来一个结构性推论overlay 比 panel 更平所以panel 不能放进 overlay 内部——否则它的角会向外凸出。规则六已经排除了这种情形对话框里的表单不是面板。规则四颜色是令牌永远不是值颜色体系是整个规范中最深的一层可以拆成几条子规则1两级文字、三级表面、两级线条。文字只用text-ink内容本身与text-ink-2关于内容的说明不存在第三档更淡的文字——注释、时间戳、占位符的次要地位已经由位置和字号交代过了再淡一档只会损害可读性。线条是border-line与border-line-strong。填充面是bg-surface静止、bg-surface-2悬停、bg-surface-3选中。2五个语义色只在有意义处出现。ok、warn、danger、contest、violet这五个语义色只用于状态、争议边、破坏性操作绝不作为装饰。neutral-500、white/10、rose-400、[var(--u-…)]这类写法在页面中一律禁止令牌在styles.css中定义一次通过 Tailwind 的theme暴露为颜色工具类如text-danger、bg-surface-2这是页面拿到颜色的唯一入口web/src/styles.css。3状态是彩色圆点 普通文字不是彩色胶囊。一列填色胶囊会让眼睛先读到一片颜色而不是那一列在说什么Status组件把颜色放在圆点上文字用text-ink-2。Chip则留给不是状态的东西——计数、库名、derived 这类标记它们是贴在内容上的标签需要一个盒子圈起来。对应实现可看 web/src/ui/index.tsx 中的Status与Chip组件Status渲染一个bg-ok/bg-warn/bg-danger的圆点加普通文字Chip复用 shadcn Badge 的骨架但覆盖为rounded-cell方角与项目语义色。4玻璃是表面处理不是颜色。glass用于余光中的面板glass-strong用于正在被阅读的面板两者在指针移入时都转为实底对应--u-surface-strong-hover。页面不得书写backdrop-blur。styles.css中--u-surface-strong从 0.68 调到 0.78 再到 0.86 的注释记录了这一演变加上saturate(0.3)去色之后半透明像纸的腻感消失于是可以吃回厚度web/src/styles.css。5值只存在于令牌块主题切换有两条轨道。一个颜色值#rrggbb、rgba(…)不允许出现在任何.ts/.tsx文件或styles.css的规则里只允许出现在令牌块中。令牌分两个家族本项目令牌--u-*ink、线条、表面、语义色以及画布绘制所需的一切。暗色在:root浅色在:root[data-themelight]shadcn 令牌--background、--border、--input、--ring等shadcn 组件读取的值。浅色在:root暗色在.dark。web/src/theme.ts 在切换主题时同时在html上设置data-theme与.dark类——这样从 shadcn registry 取来的组件无需任何修改就能跟随主题而画布与语义色继续读取data-theme。主题选择system/light/dark存放在浏览器localStorage的utopia.theme键中不经过后端initTheme还会监听系统偏好变化仅当用户选择system时。6只有两个文件允许读取令牌值。web/src/pages/graphVisuals.ts 通过getComputedStyle(document.documentElement).getPropertyValue(name)读取画布需要的令牌——canvas 无法解析var()且主题变化时需要重新读取web/src/palette.ts 持有实体颜色属于数据必须与 crates/utopia-store/src/palette.rs 中的ENTITY_PALETTE逐字节一致Rust 端有测试盯着改漏了会红。一个细节很有说服力值被读回来时是压缩器处理过的形态#ffffff变成#fffrgba(176,120,20,0.6)变成#b0781499所以读者函数必须解析每一种CSS 颜色拼写一个只认识六位 hex 的解析器会静默回退为灰色——这正是浅色主题下所有图谱节点一度渲染成灰色团块的原因。白色或黑色的阴影写作rgba(var(--u-ink-rgb), α)/rgba(var(--u-ground-rgb), α)alpha 留在使用处alpha 描述层级三元组才随主题切换rgba(0,0,0,0)是透明而非颜色允许通过。规则五状态活在组件里hover、focus、active、disabled 与动效只在组件中定义一次页面绝不书写hover:、focus:、transition或duration-。具体要求每个控件都有可见的焦点环键盘可达性底线每个禁用控件都要变暗opacity-50且不响应交互每个 hover 进入动效为--u-fast120ms离开为--u-base260ms。具体而言页面不渲染任何原始button、input、textarea或select而是使用壳层导出的Button、IconButton、Input、Textarea、Dropdown、SearchSelect、MenuSelect。项目里没有任何原生select它的弹出层由操作系统绘制、无法主题化会在一页里出现两种下拉。小而有界的枚举用Dropdown成百上千的选项本体的类、部署中的人用SearchSelect。确认操作走DangerConfirm或Dialog不用window.confirm悬停提示用Tooltip不用 span 上的裸title。style-guard.mjs对这条规则的执行非常严格raw-control规则扫描button|textarea|select与input文件选择框除外state-in-page规则匹配hover:、focus:、transition、duration-native-confirm规则禁止window.confirm/alertweb/scripts/style-guard.mjs。壳层组件在 web/src/ui/index.tsx 中实现例如Button用四个项目语义变体primary/secondary/ghost/danger映射到 shadcn 的default/outline/ghost/destructiveInput额外提供icon图标槽与bare无皮形态装在别的面里两个 shadcn 没有的能力。规则六面板是内容的槽前五条规则回答面板长什么样第六条回答什么时候该有面板、周围的东西怎么摆放。一个面板装几样同类的东西——表格的行、列表的项。一组表单字段、一段正文、页面主区域里的唯一内容都不需要面板。页面本身就是它们的容器边框、填充和圆角都在宣称这是一个与周围分离的对象花在单个对象上毫无信息量只会压平周围的层级。列表是一个带行的面板不是一行一张卡片。每项一张卡片会在同一层级上摆出七八个盒子每张卡片同时充当面板和可点击对象——槽因此获得了它不该有的 hover 状态。用行的话hover 属于行hover:bg-surface-2即 web/src/ui/index.tsx 中 Table 的既有模式面板从不响应指针。记录列表就是表格。如果每行携带同样的字段成员、令牌、知识库、规则就应该跨行阅读同一事实必须落在同一列Table/Th/Td计数用u-num右对齐。替代方案每行堆一个名字、几个芯片、一行点分隔的小字会让同一事实在每行处于不同的水平位置无从比较。行内操作收敛为一个图标。每一行都写出完整动作文字会让remove和deactivate成为页面上最响亮的词。行尾的单个图标在指针停留该行时显现REVEAL配合行上的group菜单打开期间保持可见它打开一个菜单DropdownMenu如会话列表或一个对话框FormDialog如成员表的铅笔按钮。只有一个显然操作的行如停用账户上的 Restore可以直接显示为按钮。REVEAL的实际使用可见于 web/src/pages/Members.tsx、web/src/pages/Graph.tsx 等页面。作用于面板内容的控件过滤、搜索、排序、分页位于面板之外放在页头或面板上方——它们不是内容当过滤器把列表清空时面板必须呈现空状态而不能顺手带走唯一改变过滤器的途径。过滤器栏是一行搜索框在最前w-64带放大镜然后是下拉框。下拉标签里的计数放在括号中Pending (3)而不是分隔点之后——点表示和计数不是第二个字段。以决策结尾的卡片Review 队列的卡片把操作放进一个统一的页脚形态CARD_ACTIONS操作位于左下角、贴着卡片左缘这样一列卡片的每个决策都垂直对齐指针几乎不用移动页脚允许换行因为有些卡片有五六个选项破坏性操作放在最后因为最左的位置是指针最先到达的地方。解释这张卡片的内容这对为什么在队列里、当前阶段、agent 的建议放在内容上方、先于决策被读到而不是按钮旁边的角落里。面板也可以是一个动作的管辖范围设置卡片SettingsCard的页脚里放 Save它只提交边框圈住的字段。边框的价值就是回答这个按钮发送什么——一页这样的卡片就是一页独立的小保存而不是一个底部单按钮、悄悄提交整屏字段的长表单。只有一个这种卡片的页面不需要它页面本身就是边界。设置页等读一列字段的页面居中且限宽mx-auto max-w-4xl不拉伸到窗口。例外Graph 与 Ontology 上的浮动面板。它们是画布上方的glass-strong表面职责是压住画布以便阅读——这是另一个问题。这类浮动面板只展示、不编辑类、属性、实体、事实的区间在这里阅读一切变更创建、编辑、删除、连接都打开FormDialog。对话框有一个标题和一个表单Cancel / Save 在右下破坏性操作单独放在左下——通常是一个也可以是多个权重不同的操作成员的 Deactivate 切断其整个部署的访问而 Remove 只把它移出当前工作区。面板关闭键旁边的铅笔是进入编辑的途径。专用对话框位于 web/src/pages/ontologyDialogs.tsx 与 web/src/pages/graphDialogs.tsx。这些规则如何被执行style-guard 与 CIweb/scripts/style-guard.mjs 扫描web/src/**/*.{ts,tsx}命中任一规则即以非零退出码失败type-scale禁止 Tailwind 默认字号与任意 px 字号unknown-texttext-*只能是五档字号、颜色令牌或对齐/换行工具其余没有定义raw-palette禁止text-neutral-500、bg-rose-400等色板写法raw-white白与黑不是令牌深色块上的字用on-accent/on-dangertoken-by-hand禁止text-[var(--u-danger)]——令牌已经是 Tailwind 颜色radius圆角只能是四档角色名rounded-none除外它表示顶到边spacing禁止半档与任意值间距raw-control页面不得出现原生控件标签state-in-page页面不得写 hover / focus / transition / durationraw-colour色值只允许出现在令牌块例外是两个读者文件与测试 fixtureraw-shadow阴影只有u-lift/u-lift-strong两档native-confirm禁止window.confirm。几个重要的执行细节块注释被忽略所以规则可以在注释中被引用和解释raw-colour规则跳过 web/src/pages/graphVisuals.ts 与 web/src/palette.ts 两个读者文件以及*.test.ts(x)fixturesrc/components/ui/shadcn 生成层整体跳过守卫中以VENDOR标记——它们按 Tailwind 原生刻度书写由shadcn add重新生成不手工编辑但使用这些组件的页面照常受检spacing与raw-control、state-in-page等规则不作用于src/ui/组件目录——组件的内部状态本来就是组件的事存在一个迁移名单style-guard.baseline.json尚未迁移的页面暂时豁免每迁一页就删一行名单只允许缩短新文件从第一个提交起就受检。守卫在 CI 中先于构建运行web任务的pnpm build脚本为node scripts/style-guard.mjs tsc --noEmit vite buildweb/package.json破坏规则的页面无法合入。也可以单独运行pnpm guard本地检查。样式从何而来壳层与 shadcn 的职责划分web/src/ui/index.tsx 是壳层web/src/components/ui/ 是 shadcn。页面按动作的重量调用壳层——Button variantprimary | secondary | ghost | danger——壳层再映射到 shadcn 的变体secondary变为 shadcn 的outlinedanger变为destructive。因此页面在观感迁移到 shadcn 时无需改动未来预设更换也不会改。无法使用组件的地方壳层导出类字符串buttonLike、chipLike。壳层保留而 shadcn 没有的能力包括图标槽与Input的bare形态、浮在图谱画布上的输入框所需的实底填充、Status、MenuSelect、CARD_ACTIONS以及FormDialog的一或若干危险操作。新控件以全部五种状态加入壳层后再使用新 shadcn 组件用shadcn add添加——绝不粘贴进来手工修改因为下一次add会覆盖掉修改。实践建议与延伸阅读想快速验证页面是否合规在web/目录运行pnpm guard或直接查看pnpm build中守卫先于tsc与vite build的执行顺序。想理解令牌如何落到 Tailwind阅读 web/src/styles.css 的theme块字号、颜色、圆角全部在此定义一次与:root/:root[data-themelight]两个令牌族。想观察规则的实战形态对比 web/src/pages/Members.tsx行内操作REVEAL、表格化列表、web/src/pages/Graph.tsx画布浮动面板与 web/src/pages/ontologyDialogs.tsx / web/src/pages/graphDialogs.tsx只读面板 表单对话框的分离。想了解数据色为何必须与后端一致对照 crates/utopia-store/src/palette.rs 的ENTITY_PALETTE、color_for_keyFNV-1a 确定性哈希跨进程稳定与前端 web/src/palette.ts两处配色必须逐字节相同并有测试守护。这六条规则的价值不在于把界面锁死而在于让界面语言本身成为可讨论、可检查、可演进的对象数值收敛为令牌、角色取代数字、状态内聚于组件最终由一段不足 230 行的正则脚本守住整条 CI。对任何希望建立长期一致前端规范、并让规范真正可执行的项目而言Utopia 的这套文档 令牌 守卫组合是一个值得对照的实现样本。赞分享后端前端人工智能RAG知识图谱知识管理搜索引擎【免费下载链接】utopiaWorlds first open-source enterprise world model.项目地址https://gitcode.com/gh_mirrors/ont/utopia点击查看免费下载相关推荐gbrain 设计系统解析从 Voice 规则、设计 Token 到服务端 SVG 图表的完整设计规范gbrain 设计系统解析从 Voice 规则、设计 Token 到服务端 SVG 图表的完整设计规范 gbrain 的 DESIGN.md 是管理后台ad人工智能RAGAgent 记忆MCP 服务知识管理Zulip前端设计系统组件库建设与设计规范制定Zulip前端设计系统组件库建设与设计规范制定 Zulip作为开源团队聊天工具其前端设计系统支撑着复杂的实时交互场景与多端适配需求。本文将从组件库架构、设计即时通讯后端前端WebSocketLangflow 前端代码质量规则深度解析cn()、设计令牌体系与状态管理规范Langflow 前端代码质量规则深度解析cn 、设计令牌体系与状态管理规范 本文基于 Langflow 仓库中的前端代码质量规则目录 code qualit人工智能大模型AI AgentRAG后端前端MCP 服务工作流自动化上一篇完整教程用 Taro UI 四步搭好一个电商小程序首页下一篇Komorebi 五分钟装好 Linux 动态壁纸创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表