如何用设计 Token 统一前后台风格

【这是一篇写在此项目开发期的文章】 一个博客项目同时有 Nuxt 前台与 Vue 后台,如何避免两套前端各写各的样式?我将在这篇文章讲讲如何将「设计 Token」抽成唯一事实来源、用三层架构统一前后台,并选择落地极简毛玻璃风格的完整过程。

如何用设计 Token 统一前后台风格

如何用设计 Token 统一前后台

主要问题:项目中的两套前端风格难以统一

我的博客最开始就有两个前端:面向读者的博客前台(后来迁到 Nuxt 3)和面向自己的管理后台(Vue 3 + Element Plus)。它们写出来的时间不同,这也导致二者风格在一开始就有所差异。

最典型的矛盾是颜色和圆角:

  • 前台写的链接色是 #475569,后台 Element Plus 默认主题跑起来却是蓝紫色;
  • 前台卡片圆角 12px,后台同一块交互又变回 8px;
  • 想让后台也"看起来像自己"时,只能手动去后台 CSS 里搜出一个个 HEX 值照着抄——然后抄多了就漏,漏了就歪

更麻烦的地方是在毛玻璃风格。后台想用半透明背景 + 模糊,前台也想要,于是两处各自写了 backdrop-filter: blur(12px),到想统一调整时,得同时改两个工程里的好几处。

所以当时遇到的问题就是在统一二者的视觉风格时,需要额外做很多细琐的工作,并且任何视觉决策都只能靠记忆。

解决方案:把设计决策集中成三段式 Token

我给这套体系起名「设计 Token」(Design Tokens),并定了原则:禁止在组件里硬编码色值和尺寸。所有决策收纳进一份文档和三段式的变量文件。

第一层:原始 Token(primitive)

原始 Token 是最底层原子值,不带任何语义,算是这个项目可用的素材库。前台在 abstracts/_variables.scss

// 中性色灰度
$color-gray-500: #adb5bd;
$color-gray-600: #868e96;

// 语义色
$color-success: #52c41a;
$color-warning: #faad14;

// 间距 — 4px 网格
$spacing-unit: 0.25rem; // 4px 基础单位
$spacing-1: $spacing-unit;
$spacing-2: $spacing-unit * 2;

// 圆角
$border-radius-sm: 0.25rem;  // 4px
$border-radius-md: 0.75rem;  // 12px

这一层的价值主要是可复用、可枚举。到时候写的时候可以不用 border-radius: 12px,而是用 $border-radius-md 来代替。

第二层:语义 Token(semantic)

语义 Token 才真正对应设计意图——它用来规范"主色、危险色、卡片背景分别是什么"。这一层是 CSS 变量,由主题所驱动(亮色 :root、暗色 html.dark),是运行时真正会被读取的:

:root {
  --bg-card: rgba(255, 255, 255, 0.8);
  --text-primary: #1e293b;
  --text-secondary: #475569;
  --border-color: rgba(203, 213, 225, 0.7);
  --color-accent: #475569;            /* 主品牌色:链接、强调、激活态 */
  --color-accent-deep: #1e293b;       /* 深强调:标题、重按钮 */
  --shadow-card: 0 1px 3px rgba(0,0,0,.06), 0 4px 16px rgba(0,0,0,.04);
}

组件永远只用语义层。这样切换亮暗主题、或整体换一个主色,只需改这一层,不用动任何一个组件

第三层:组件级(component)

组件层是"语义 Token 的具体组合",由 Sass placeholder / mixin 承载。比如毛玻璃卡片,它把背景、模糊、边框、圆角四个语义 Token 拼成一个可复用类:

.glass-card {
  background: var(--bg-card);
  backdrop-filter: blur(var(--glass-blur));
  -webkit-backdrop-filter: blur(var(--glass-blur));
  border: 1px solid var(--glass-border);
  border-radius: $border-radius-lg;
}

编译期 Sass + 运行时 CSS 变量,两层分工

很多人可能会纠结"到底用 Sass 变量还是 CSS 变量"。而我则是打算都用,并且二者各管一段。

  • 原始 Token(颜色、间距、字号、圆角、断点、z-index)用 Sass,因为它们是"素材",编译期就能定死,不需要在浏览器里换来换去,用 Sass 还能做 color.adjust() 这类计算。
  • 语义 Token(背景、文字、边框、阴影、主题色)用 CSS 变量,因为它们是随主题变化的,必须运行时读取,才能实现 html.dark 一键切换,也才能让后台配置驱动的主色注入进来。

入口 main.scss@use 明确分层,顺序就是依赖顺序:

// 1. 抽象层(不输出 CSS)
@use "abstracts/variables" as *;
@use "abstracts/mixins" as *;
@use "abstracts/functions" as *;
@use "abstracts/placeholders" as *;

// 2. 主题变量(输出 CSS 变量)
@use "themes" as *;

关键在于abstracts/variables 里的原始 Token 是纯 Sass 值、不生成任何 CSSthemes 才把语义层输出成 :root / html.dark 的 CSS 变量。编译出来既无冗余,又能运行时调主题。

毛玻璃风格

毛玻璃(Glassmorphism)是我目前为博客选择的视觉风格,也是最容易"做成花哨却难用"的一环。落实到 Token 上,前台只暴露三个变量:

$glass-background-light: rgba(255, 255, 255, 0.8);
$glass-background-dark: rgba(26, 26, 46, 0.65);
$glass-border-light: rgba(229, 229, 229, 0.85);
$glass-border-dark: rgba(42, 58, 92, 0.85);
$glass-blur: 12px;   // 当时用的是高模糊

对应的运行时变量(亮色):

--glass-blur: 12px;
--glass-border: rgba(255, 255, 255, 0.5);

我的思路是高透明 + 高亮玻璃边 + 主题化阴影

  1. 透明背景是主视觉。亮色 rgba(255,255,255,0.8)、暗色 rgba(26,26,46,0.65),能够让背景图/内容透过来。
  2. 柔和高光玻璃边--glass-border)勾勒轮廓,能够让卡片从背景中区分出来。
  3. 主题化阴影分层(--shadow-card / --shadow-elevated)提供景深,暗色下阴影加深以维持层次。

backdrop-filter 只用在卡片、导航这类独立层上,整页背景禁用——因为模糊整个页面会让滚动和渲染都变卡。

毛玻璃在移动端的问题

在最初的设计中,我发现移动端设备性能较弱,而手指一滑就要实时模糊大片区域,体验会明显掉帧。所以我最终决定移动端(≤768px)对大面积玻璃进一步降级为纯色、去掉模糊,只保留透明度和高光边维持层次。

毛玻璃在后台的问题

我当时也将毛玻璃风格应用到了后台,但是最终选择了弃用。主要是有以下几点原因:

  • 后台充斥着高密度的表格、表单、状态色,需要稳定、扎实的背景做支撑。半透明会让文字跟下面的内容打架,极大地影响可读性。
  • 后台一次要渲染大量表格和数据,大面积 backdrop-filter 的代价远比前台高,一上来就容易掉帧。
  • 高模糊 blur(12px+) 在设备上又费性能、又会糊出噪点;当背景是渐变装饰图时,卡片会看起来"蒙了一层灰",文字对比度被进一步拉低——这在表格密集的后台尤其明显。

结论:只在前台保留毛玻璃

走过这一圈,我最终给毛玻璃的定位是:毛玻璃是面向读者的"前台视觉语言",不适合后台这种高密度工具型界面。 所以后台弃用了大面积毛玻璃,只在 header、侧边栏这类局部用半透明点缀;同时保留 token 命名,方便日后整体换肤时不至于完全脱离体系。

但是在使用毛玻璃时我又遇到了新的一些问题,比如说它会影响到博客背景图的展示读者对文字内容的阅读等,接下来我会研究一下看看有没有更好的解决方案。

前后台怎么共享一套体系

聊完玻璃的取舍与问题,让我们回到体系本身:前后台命名体系和三段的骨头是同构的——后台同样有整套原始 Token、语义 Token、组件级复用,只是语义层取值更偏向"实底"。

这样的做法主要有两点好处:

  • 降低沟通成本:前后台提到某个东西,说"--bg-card"就行,不用解释是哪个 HEX。
  • 单点演进:想全局加一档圆角、改主色,可以直接去 Token 加/改,而不是去三个文件里找散落的属性。
浙ICP备2026077668号

欢迎来到我的网站 这个网站主要是作技术和生活两个维度的记录