
设计系统从设计稿到 CSS 变量设计系统不是把设计稿里的颜色抄一遍。它是设计稿与代码之间的翻译层——一次定义全局生效改一个变量就能换肤。这篇文章把从 Design Token 到 CSS 变量再到 Element Plus 主题覆写的完整链路理清楚。一、为什么 17 个 Frame 需要一套设计系统在上一篇文章里我从 Figma 中提取出了颜色、字体、圆角、间距等设计属性。但提取只是第一步——如果直接把这些值写死在每个组件里会发生什么假设主色#0073FB出现在了 17 个 Frame 的 80 处节点上。按看到什么写什么的直觉做法你的代码里会出现几十次color: #0073FB、background: #0073FB、border-color: #0073FB。然后有一天设计师说“主色调调浅一点改成#1A8CFF。”你打开 VS Code全局搜索#0073FB替换为#1A8CFF。运气好的话改了 40 处没出问题。运气不好的话某个恰好也是#0073FB但不是主色的边框被你一起改掉了——这种 Bug 等你发现的时候可能已经上线一周了。这不是假设。我在之前的项目里亲身经历过一次改主题色的噩梦。从那以后我给自己定了一条铁律任何在项目中重复出现 2 次以上的样式值必须定义为变量。这不是过度工程化这是对自己的仁慈。对于管网模型工具这个项目17 个 Frame 的样式看起来眼花缭乱但经过上一篇的去重归并后真正的系统色只有 10 个左右字号只有 4 个阶梯圆角只有 4 个阶梯。这些数值不是散落的——它们构成了一套隐式的设计规范。我要做的是把这套隐式规范显式化。二、设计 Token 的五维分类设计 Token 这个词听起来高大上但本质上就是给样式值起个有意义的名字。我把这个项目涉及的 Token 分了五个维度2.1 颜色 Token颜色是最容易失控的维度。17 个 Frame 里看起来有 50 种颜色但实际上有些是同一个颜色在不同透明度下的表现比如主色#0073FB降低 10% 透明度做 hover 态有些是同一个颜色在不同节点上的重复引用有些是渐变叠加后的渲染色不是源色去重归并后真正的系统色如下Token 名称色值语义使用场景--color-bg-page#C8E1FF页面底色登录页背景、内容区底色--color-bg-card#F0F5FC卡片/容器背景顶栏、侧栏、信息卡片--color-bg-white#FFFFFF纯白背景弹窗、表格、表单内部--color-primary#0073FB主色/品牌色主按钮、选中态、链接、图标高亮--color-primary-hover#005FCF主色悬停按钮 hover、菜单 hover--color-primary-light#E8F1FE主色浅底选中行背景、标签背景--color-success#52C41A成功/启用状态标签-启用、成功提示--color-danger#FF4D4F危险/删除删除按钮、禁用状态--color-text-primary#333333主文字色标题、正文、表格内容--color-text-secondary#666666次要文字辅助说明、标签、描述--color-text-placeholder#999999占位/禁用文字输入框 placeholder、禁用态文字--color-border#D9D9D9边框色表格线、输入框边框、分割线为什么要区分--color-text-primary和--color-text-secondary举个例子一个表单里字段标签用#333333辅助说明用#666666。如果直接写色值后来设计师说辅助说明再淡一点改成#888888你又要全局搜索。定义成变量后只需改一处。2.2 字体 Token这个项目涉及三种字体MiSans中文、AlibabaPuHuiTi中文标题、Inter英文/数字。但用户设备上不一定安装了这些字体所以需要完整的降级策略Token 名称值--font-family-baseMiSans, PingFang SC, Microsoft YaHei, sans-serif--font-family-titleAlibabaPuHuiTi, MiSans, PingFang SC, sans-serif--font-family-numberInter, -apple-system, Segoe UI, sans-serif2.3 字号阶梯17 个 Frame 中字号并非随意取值而是有明显的阶梯规律Token 名称数值使用场景--font-size-xl26px登录页大标题、页面级标题--font-size-lg18px模块标题、卡片标题--font-size-md16px正文、表格内容、输入框文字--font-size-sm14px辅助文字、标签、提示信息--font-size-xs12px注释、徽标、次要元数据注意这不是随便挑了 5 个字号——这些值直接来自 Figma 中所有 TEXT 节点的fontSize字段统计结果。没有出现 13px、15px、17px 这样的奇数尺寸也没有出现 20px、22px 这样不在阶梯内的值。这说明设计师遵循了一套清晰的字号规范我只是把它翻译成了变量名。2.4 圆角阶梯Token 名称数值使用场景--radius-lg14px卡片、弹窗、大型容器--radius-md10px输入框、下拉菜单、表格--radius-sm8px标签、徽标、小按钮--radius-xs6px微小元素tooltip、badge这四个值有一个共同点都是偶数。这不是巧合——偶数值在 Retina 屏幕上渲染时不会出现半像素模糊。好的设计师会在这种细节上体现专业性。2.5 间距阶梯间距是设计中最不起眼但最影响精致度的维度。这个项目的间距体系是严格的 4px 基准体系--spacing-xs:4px;--spacing-sm:8px;--spacing-md:12px;--spacing-lg:16px;--spacing-xl:24px;--spacing-2xl:32px;--spacing-3xl:40px;--spacing-4xl:50px;你会发现4 × 2 8、4 × 3 12、4 × 4 16……除50px外所有间距都是 4 的倍数。这遵循了设计界的4px 基准网格原则确保所有元素对齐在同一个不可见的网格上视觉上整齐而不杂乱。三、tokens.css把 Token 写成代码有了 Token 定义下一步就是把它写成真正的 CSS。我创建了src/styles/tokens.css/* 设计系统 · Design Tokens 基于 Figma 管网模型工具 设计稿提取 */:root{/* ── 颜色背景 ── */--color-bg-page:#C8E1FF;--color-bg-card:#F0F5FC;--color-bg-white:#FFFFFF;/* ── 颜色品牌/功能 ── */--color-primary:#0073FB;--color-primary-hover:#005FCF;--color-primary-light:#E8F1FE;--color-success:#52C41A;--color-danger:#FF4D4F;/* ── 颜色文字 ── */--color-text-primary:#333333;--color-text-secondary:#666666;--color-text-placeholder:#999999;/* ── 颜色边框/分割 ── */--color-border:#D9D9D9;/* ── 字体家族 ── */--font-family-base:MiSans,PingFang SC,Microsoft YaHei,sans-serif;--font-family-title:AlibabaPuHuiTi,MiSans,PingFang SC,sans-serif;--font-family-number:Inter,-apple-system,Segoe UI,sans-serif;/* ── 字体字号 ── */--font-size-xs:12px;--font-size-sm:14px;--font-size-md:16px;--font-size-lg:18px;--font-size-xl:26px;/* ── 字体行高 ── */--line-height-tight:1.2;--line-height-normal:1.5;--line-height-loose:1.8;/* ── 圆角 ── */--radius-xs:6px;--radius-sm:8px;--radius-md:10px;--radius-lg:14px;/* ── 间距4px 基准网格 ── */--spacing-xs:4px;--spacing-sm:8px;--spacing-md:12px;--spacing-lg:16px;--spacing-xl:24px;--spacing-2xl:32px;--spacing-3xl:40px;--spacing-4xl:50px;/* ── 阴影 ── */--shadow-card:0 2px 8pxrgba(0,0,0,0.08);--shadow-dialog:0 4px 16pxrgba(0,0,0,0.12);--shadow-selected:0 0 0 2pxvar(--color-primary-light);/* ── 过渡动画 ── */--transition-fast:0.15s ease;--transition-normal:0.3s ease;/* ── 布局 ── */--layout-topbar-height:120px;--layout-sidenav-width:200px;--layout-content-padding:24px;}这里有几个设计决策值得展开1. 阴影也定义为 TokenFigma 中卡片有0 2px 8px rgba(0,0,0,0.08)的投影弹窗有稍重的0 4px 16px rgba(0,0,0,0.12)。如果不定义为 Token很容易出现这个弹窗的阴影比另一个深一点的不一致问题。2. 布局尺寸也纳入 Token顶栏高度 120px、侧栏宽度 200px——这些值在多个布局组件中会反复引用。把它们定义为 Token改布局时只需要改一个地方。3. 过渡动画统一所有交互hover、展开、切换使用相同的缓动曲线和时长。用户感知到的流畅度很大程度上取决于动画的一致性。四、Element Plus 主题覆写让组件库听话设计 Token 定义好了但 Element Plus 组件有自己的默认样式——蓝色主色是#409EFF圆角是3px和我们的设计稿完全不同。要让 Element Plus穿上我们的设计系统需要做两级覆写。4.1 理解 Element Plus 的样式变量体系Element Plus 2.x 使用 CSS 变量来控制主题所有变量都以--el-开头。官方文档列出了 100 个可覆写的变量但实际项目中不需要全改。核心变量就这些Element Plus 变量说明--el-color-primary主色--el-color-primary-light-3~light-9主色的浅色变体--el-border-radius-base默认圆角--el-font-size-base默认字号--el-text-color-primary主文字色--el-text-color-regular常规文字色--el-border-color-base边框色4.2 用 SCSS 覆写在src/styles/下创建element-override.scss/* Element Plus 主题覆写 将 design tokens 映射到 Element Plus CSS 变量 */ // ── 主色 ── --el-color-primary: var(--color-primary); --el-color-primary-light-3: var(--color-primary-light); --el-color-primary-light-5: var(--color-primary-light); --el-color-primary-light-7: var(--color-primary-light); --el-color-primary-light-8: var(--color-primary-light); --el-color-primary-light-9: var(--color-primary-light); --el-color-primary-dark-2: var(--color-primary-hover); // ── 文字色 ── --el-text-color-primary: var(--color-text-primary); --el-text-color-regular: var(--color-text-secondary); --el-text-color-placeholder: var(--color-text-placeholder); // ── 边框 ── --el-border-color-base: var(--color-border); --el-border-color-light: var(--color-border); // ── 圆角 ── --el-border-radius-base: var(--radius-md); --el-border-radius-small: var(--radius-sm); // ── 字号 ── --el-font-size-base: var(--font-size-md); --el-font-size-small: var(--font-size-sm); --el-font-size-extra-small: var(--font-size-xs); --el-font-size-large: var(--font-size-lg); // ── 背景 ── --el-bg-color: var(--color-bg-white); --el-bg-color-page: var(--color-bg-page);这里有一个技术细节Element Plus 的主色会衍生出light-3到light-9九个浅色变体用于 hover 背景、选中态背景等。如果不覆写它们会出现主按钮是#0073FB但 hover 时背景变成#409EFF浅色的诡异现象。我把它们统一映射到--color-primary-light虽然精度上会损失一些渐变层次但对于演示原型来说足够用了。4.3 全局样式导入顺序样式的导入顺序有严格要求。在main.js中// 1. Element Plus 默认样式必须先加载importelement-plus/dist/index.css// 2. 设计 Token定义 CSS 变量import/styles/tokens.css// 3. Element Plus 主题覆写消费 Token 变量import/styles/element-override.scss// 4. 全局自定义样式import/styles/global.css顺序错了会有问题如果在 Token 定义之前就覆写 Element PlusCSS 变量找不到对应的值会回退到浏览器默认值整个色彩体系就乱套了。五、字体加载方案CDN 本地 回退三级策略字体是这个设计系统里不可见但很重要的一环。设计师用了 MiSans小米字体和 AlibabaPuHuiTi阿里普惠体但用户的电脑上大概率没有。我的策略分三级第一级CDN 加载最优解如果字体官方提供了 CDN优先使用。CDN 的优势是不需要打包进项目省体积而且用户浏览器可能已经缓存了这些字体。/* 在 index.html 中通过 link 引入或使用 import */importurl(https://cdn.jsdelivr.net/npm/misanslatest/lib/Normal/MiSans-Normal.min.css);第二级本地打包兜底方案如果 CDN 不可用比如内网环境将字体文件放入src/assets/fonts/用font-face声明font-face{font-family:MiSans;src:url(/assets/fonts/MiSans-Normal.woff2)format(woff2),url(/assets/fonts/MiSans-Normal.woff)format(woff);font-weight:400;font-style:normal;font-display:swap;/* 先显示回退字体加载完再切换 */}第三级系统字体回退无论 CDN 还是本地打包最后的font-family声明里一定带系统字体做降级font-family:MiSans,PingFang SC,Microsoft YaHei,sans-serif;这样即使用户的设备完全没有 MiSans也能用 PingFang SCmacOS或 Microsoft YaHeiWindows渲染出视觉效果接近的中文。font-display: swap也值得单独提一句它告诉浏览器先用回退字体渲染文字等自定义字体下载完成后再替换。这避免了字体没加载完页面白屏的 FOITFlash of Invisible Text问题。六、设计系统的工程价值一套设计 Token CSS 变量 Element Plus 覆写搭完之后它带来的价值会随着项目推进越来越明显1. 改色 改一行的时代设计师说主色改成暖色系。你打开tokens.css改一行--color-primary:#FF6B35;/* 原来 #0073FB */--color-primary-hover:#E55A2B;/* 原来 #005FCF */--color-primary-light:#FFF0E8;/* 原来 #E8F1FE */刷新页面。整个项目的所有主按钮、选中态、链接、图标高亮、标签背景——从登录页到模型发布的每一个 Frame——全部变成暖色系。一秒完成。这就是一次定义全局生效的真正含义。2. 新页面开发成本骤降当你开发第 8 篇的API 注册管理页面时你不需要再去翻 Figma 看表格的边框是什么颜色、“弹窗的圆角是多少”、“间距应该用 16px 还是 24px”。这一切都在 Token 里style scoped .api-table { background: var(--color-bg-white); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--spacing-xl); } .api-table .header { color: var(--color-text-primary); font-size: var(--font-size-lg); margin-bottom: var(--spacing-lg); } /style你写的不是这个页面怎么设计而是这个页面用哪个 Token。新页面开发变成了一场Token 拼图——而拼图比从零画图快得多。3. 团队协作的语言统一如果是多人开发设计系统还有一个隐藏价值统一沟通语言。设计师说这个按钮用主色前端直接写var(--color-primary)。不需要问主色是哪个色值来着不需要对着 Figma 取色器手动吸色。设计师和前端说的是同一套 Token 语言沟通成本直线下降。七、一个文件搞定给读者的设计系统鸡尾酒最后如果你不想像我这样拆成tokens.csselement-override.scssglobal.css三个文件可以把它们合并成一个theme.css直接引入。对于个人项目或小团队来说一个文件反而更方便维护。我把三合一版本也放在这里你可以根据自己的项目规模选择哪种组织方式/* theme.css 设计系统 Element Plus 覆写一站式 放置于 src/styles/theme.css 在 main.js 中 element-plus/dist/index.css 之后引入 */:root{/* 设计 Token */--color-bg-page:#C8E1FF;--color-bg-card:#F0F5FC;--color-bg-white:#FFFFFF;--color-primary:#0073FB;--color-primary-hover:#005FCF;--color-primary-light:#E8F1FE;--color-success:#52C41A;--color-danger:#FF4D4F;--color-text-primary:#333333;--color-text-secondary:#666666;--color-text-placeholder:#999999;--color-border:#D9D9D9;--font-family-base:MiSans,PingFang SC,Microsoft YaHei,sans-serif;--font-family-title:AlibabaPuHuiTi,MiSans,PingFang SC,sans-serif;--font-family-number:Inter,-apple-system,Segoe UI,sans-serif;--font-size-xs:12px;--font-size-sm:14px;--font-size-md:16px;--font-size-lg:18px;--font-size-xl:26px;--line-height-tight:1.2;--line-height-normal:1.5;--line-height-loose:1.8;--radius-xs:6px;--radius-sm:8px;--radius-md:10px;--radius-lg:14px;--spacing-xs:4px;--spacing-sm:8px;--spacing-md:12px;--spacing-lg:16px;--spacing-xl:24px;--spacing-2xl:32px;--spacing-3xl:40px;--spacing-4xl:50px;--shadow-card:0 2px 8pxrgba(0,0,0,0.08);--shadow-dialog:0 4px 16pxrgba(0,0,0,0.12);--shadow-selected:0 0 0 2pxvar(--color-primary-light);--transition-fast:0.15s ease;--transition-normal:0.3s ease;--layout-topbar-height:120px;--layout-sidenav-width:200px;--layout-content-padding:24px;/* Element Plus 覆写 */--el-color-primary:var(--color-primary);--el-color-primary-light-3:var(--color-primary-light);--el-color-primary-light-5:var(--color-primary-light);--el-color-primary-light-7:var(--color-primary-light);--el-color-primary-light-8:var(--color-primary-light);--el-color-primary-light-9:var(--color-primary-light);--el-color-primary-dark-2:var(--color-primary-hover);--el-text-color-primary:var(--color-text-primary);--el-text-color-regular:var(--color-text-secondary);--el-text-color-placeholder:var(--color-text-placeholder);--el-border-color-base:var(--color-border);--el-border-color-light:var(--color-border);--el-border-radius-base:var(--radius-md);--el-border-radius-small:var(--radius-sm);--el-font-size-base:var(--font-size-md);--el-font-size-small:var(--font-size-sm);--el-font-size-extra-small:var(--font-size-xs);--el-font-size-large:var(--font-size-lg);--el-bg-color:var(--color-bg-white);--el-bg-color-page:var(--color-bg-page);}八、小结设计系统是可维护性的入场券如果说上一篇的读 Figma是从混沌中提取秩序那这一篇的搭设计系统就是把那个秩序固化下来。它不会让你的页面立刻变好看——Token 本身不是视觉效果它只是变量的名字。但它是整个项目可维护性的基础。没有它你每写一个颜色都要犹豫是这个色值吗改一个色值要担心会不会影响其他地方。设计系统的价值不在写的那一刻而在改的那一刻。Token → CSS 变量 → Element Plus 覆写这三层下来你的项目就拥有了一套完整的翻译系统设计师的语言通过 Figma 进入 TokenToken 通过 CSS 变量进入组件组件通过覆写进入 Element Plus。从此以后还原设计稿不是一场手工作坊式的复制粘贴而是一次工程化的映射。上一篇03 - Figma 数据提取用 MCP 工具读懂设计稿下一篇预告设计系统就位了下一步是什么搭脚手架——用 Vite 5 分钟搭出标准项目结构把 Token、路由、状态管理、Mock 数据全部各就各位。