尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

新特性:设计系统(Design System)构建 —— 建立团队的 Token 变量库

新特性:设计系统(Design System)构建 —— 建立团队的 Token 变量库
📅 发布时间:2026/7/14 19:24:04


文章目录

    • 前言
    • 一、为什么需要 Token 变量库?
      • 1.1 硬编码的四大问题
      • 1.2 Token 解决什么?
      • 1.3 与现有 DSTokens.ets 的关系
    • 二、HarmonyOS 资源体系速览
      • 2.1 目录结构
      • 2.2 引用语法
      • 2.3 单位约定
    • 三、命名规范:语义化,而非描述色值
      • 3.1 推荐格式
      • 3.2 好的命名 vs 坏的命名
      • 3.3 完整 Token 清单(Demo)
    • 四、定义颜色 Token
      • 4.1 base/element/color.json
      • 4.2 dark/element/color.json
      • 4.3 对比一览
    • 五、定义间距与圆角 Token
      • 5.1 float.json 完整定义
      • 5.2 4vp 网格原则
      • 5.3 引用示例
    • 六、在组件中消费 Token
      • 6.1 迁移前 vs 迁移后
      • 6.2 Demo 组件画廊
      • 6.3 色板数据驱动
    • 七、暗色模式:零代码适配
      • 7.1 原理
      • 7.2 开发注意事项
      • 7.3 容器色的暗色处理
    • 八、团队落地工作流
      • 8.1 设计 → 开发协作
      • 8.2 渐进式迁移策略
      • 8.3 与 @Styles / @Extend 结合
      • 8.4 Code Review 检查项
    • 九、常见误区
      • 9.1 「Token 就是常量类」
      • 9.2 「暗色模式在组件里判断」
      • 9.3 「Token 名用色值描述」
      • 9.4 「间距随意定」
      • 9.5 「只定义亮色」
    • 十、Token 规模参考
    • 十一、总结

前言

打开一个三个月前写的页面,你可能会看到:

.fontColor('#6B7280').padding(12).borderRadius(10).backgroundColor('#2563EB')

当时觉得「就几个魔法数字,记得住」。三个月后品牌色从#2563EB换成#0A59F7,设计师要求暗色模式适配,间距从 12 统一调到 16——你需要在几十个文件里全文搜索替换,漏一处就是视觉不一致。

这就是没有设计系统的代价。

Design System(设计系统)的核心产物之一,是Token 变量库——把颜色、间距、圆角、字号等视觉属性抽象成语义化变量,集中定义在resources目录,组件通过$r('app.color.ds_text_primary')引用,而非硬编码。

HarmonyOS 的资源体系(base+dark双目录)天然支持主题切换。本文结合项目 Demo,从 Token 理念、资源定义、引用方式、暗色适配到组件落地,做一次完整实战解析。读完后,你应该能回答:

  • 什么是语义化 Token,和直接写色值有什么区别?
  • 颜色、间距、圆角分别放在哪个 JSON 文件?
  • $r()如何在 ArkUI 组件中使用?
  • 如何实现「改一处 resources,全 App 换肤」?

一、为什么需要 Token 变量库?

1.1 硬编码的四大问题

问题后果
散落各处同一个#2563EB出现 80 次,改品牌色要改 80 处
语义缺失#6B7280是标题色还是提示色?新人无法判断
无法换肤暗色模式需在每个组件写if (isDark)
协作断裂设计师给 Figma Token,开发手写 hex,两边对不上

1.2 Token 解决什么?

Token 是设计决策的命名快照:

设计师说:「主文本用 text-primary」 开发写: .fontColor($r('app.color.ds_text_primary')) 资源文件:ds_text_primary = #111827(亮色)/ #F9FAFB(暗色)

改色?只改color.json。加暗色?只加dark/color.json同名条目。组件代码一行不动。

1.3 与现有 DSTokens.ets 的关系

项目中designsystem/tokens/DSTokens.ets用 TypeScript 常量定义 Token:

exportclassDSColor{staticreadonlybrandPrimary='#0A59F7'}

这适合纯逻辑层或跨 module 编译期引用,但不支持系统级主题切换——暗色模式来了要手动判断。本文推荐的resources Token则由框架在运行时按主题自动选取,是 HarmonyOS 官方推荐的视觉变量管理方式。两者可并存:resources 管颜色/尺寸,ETS 类管枚举和组件变体。


二、HarmonyOS 资源体系速览

2.1 目录结构

entry/src/main/resources/ ├── base/ ← 默认(亮色)资源 │ └── element/ │ ├── color.json ← 颜色 Token │ ├── float.json ← 间距/圆角/字号 Token │ └── string.json └── dark/ ← 暗色覆写 └── element/ └── color.json ← 同名 Token,暗色值

系统根据当前主题自动选择base或dark目录下的同名资源。组件始终引用$r('app.color.ds_text_primary'),无需关心当前是亮是暗。

2.2 引用语法

// 颜色.fontColor($r('app.color.ds_text_primary')).backgroundColor($r('app.color.ds_bg_surface'))// 间距 / 圆角 / 字号.padding($r('app.float.ds_space_lg')).borderRadius($r('app.float.ds_radius_md')).fontSize($r('app.float.ds_font_body1'))

格式:$r('app.<type>.<name>'),其中type对应 JSON 文件名(color、float、string)。

2.3 单位约定

文件常用单位用途
color.json#RRGGBB颜色值
float.jsonvp间距、圆角(随屏幕密度缩放)
float.jsonfp字号(随系统字体缩放)

Demo 间距用vp,字号用fp,符合 HarmonyOS 适配规范。


三、命名规范:语义化,而非描述色值

3.1 推荐格式

ds_<category>_<role>
部分含义示例
ds_设计系统前缀,避免与业务资源冲突—
category类别text、bg、space、radius
role语义角色primary、secondary、md、lg

3.2 好的命名 vs 坏的命名

✅ 语义化❌ 描述性
ds_text_primaryds_gray_900
ds_brand_primaryds_blue_500
ds_bg_surfaceds_white
ds_space_mdds_twelve_vp

语义化命名的好处:品牌色从蓝变绿,Token 名ds_brand_primary不用改,只改值。ds_blue_500则名实不符,制造混乱。

3.3 完整 Token 清单(Demo)

颜色(17 个):

类别Token
品牌/语义ds_brand_primary,ds_brand_primary_container,ds_success,ds_success_container,ds_warning,ds_warning_container,ds_danger,ds_danger_container
文本ds_text_primary,ds_text_secondary,ds_text_tertiary,ds_text_inverse
表面ds_bg_page,ds_bg_surface,ds_bg_elevated,ds_border_default,ds_border_strong,ds_divider

间距(6 级,4vp 网格):

Token值
ds_space_xs4vp
ds_space_sm8vp
ds_space_md12vp
ds_space_lg16vp
ds_space_xl24vp
ds_space_xxl32vp

圆角(5 级):

Token值典型场景
ds_radius_sm6vp小标签、徽标
ds_radius_md10vp按钮、输入框
ds_radius_lg16vp卡片、面板
ds_radius_xl24vp底部弹窗
ds_radius_full999vp胶囊、圆形

字阶(6 级):

Token值
ds_font_caption10fp
ds_font_body212fp
ds_font_body114fp
ds_font_subtitle16fp
ds_font_title20fp
ds_font_headline24fp

四、定义颜色 Token

4.1 base/element/color.json

{"color":[{"name":"ds_brand_primary","value":"#2563EB"},{"name":"ds_text_primary","value":"#111827"},{"name":"ds_bg_page","value":"#F9FAFB"},{"name":"ds_bg_surface","value":"#FFFFFF"}]}

容器色(*_container)是 Material Design 3 风格:主色的低饱和背景,用于标签、选中态、提示区块。Demo 中ds_brand_primary_container = #EFF6FF,与ds_brand_primary = #2563EB配对使用。

4.2 dark/element/color.json

暗色目录中定义同名 Token,赋予不同值:

{"color":[{"name":"ds_text_primary","value":"#F9FAFB"},{"name":"ds_bg_page","value":"#111827"},{"name":"ds_bg_surface","value":"#1F2937"},{"name":"ds_brand_primary","value":"#60A5FA"}]}

注意暗色模式下品牌色通常提高亮度(#2563EB→#60A5FA),而非简单反转,以保证对比度和可读性。

4.3 对比一览

Token亮色 (base)暗色 (dark)
ds_text_primary#111827#F9FAFB
ds_text_secondary#6B7280#9CA3AF
ds_bg_page#F9FAFB#111827
ds_bg_surface#FFFFFF#1F2937
ds_brand_primary#2563EB#60A5FA

Demo 在「色彩 Token」场景底部有暗色模式提示:切换系统暗色后,所有引用$r('app.color.ds_*')的组件自动更新,无需任何代码修改。


五、定义间距与圆角 Token

5.1 float.json 完整定义

{"float":[{"name":"ds_space_xs","value":"4vp"},{"name":"ds_space_sm","value":"8vp"},{"name":"ds_space_md","value":"12vp"},{"name":"ds_space_lg","value":"16vp"},{"name":"ds_space_xl","value":"24vp"},{"name":"ds_space_xxl","value":"32vp"},{"name":"ds_radius_sm","value":"6vp"},{"name":"ds_radius_md","value":"10vp"},{"name":"ds_radius_lg","value":"16vp"},{"name":"ds_radius_xl","value":"24vp"},{"name":"ds_radius_full","value":"999vp"}]}

5.2 4vp 网格原则

间距刻度以4vp 为基数:4、8、12、16、24、32。这保证:

  • 所有间距是 4 的倍数,视觉节奏统一
  • 设计师在 Figma 中用 8px 网格,开发用 8vp,心算对齐
  • 避免padding(13)这种「拍脑袋」数值

Demo「间距 Token」场景用色条直观展示各级长度,并附有 padding 实战卡片。

5.3 引用示例

// 统一内边距.padding($r('app.float.ds_space_lg'))// 方向性内边距.padding({left:$r('app.float.ds_space_md'),right:$r('app.float.ds_space_md'),top:$r('app.float.ds_space_sm'),bottom:$r('app.float.ds_space_sm')})// 圆角.borderRadius($r('app.float.ds_radius_lg'))// Column 间距Column({space:12})// 注意:space 参数目前只接受 number,暂不能用 Resource

最后一个注意点:Column({ space })等布局参数的space在某些 API 版本只接受number,不支持$r()。这类场景可暂时保留数字,或在@Builder封装层统一处理。颜色、padding、borderRadius、fontSize 等属性已广泛支持 Resource 类型。


六、在组件中消费 Token

6.1 迁移前 vs 迁移后

// ❌ 硬编码Text('标题').fontSize(22).fontColor('#111827')Column().padding(16).backgroundColor('#FFFFFF').borderRadius(12)// ✅ Token 驱动Text('标题').fontSize($r('app.float.ds_font_headline')).fontColor($r('app.color.ds_text_primary'))Column().padding($r('app.float.ds_space_lg')).backgroundColor($r('app.color.ds_bg_surface')).borderRadius($r('app.float.ds_radius_lg'))

6.2 Demo 组件画廊

「组件应用」场景展示四类组件,全部零硬编码:

按钮:

Text('主按钮').fontColor($r('app.color.ds_text_inverse')).backgroundColor($r('app.color.ds_brand_primary')).borderRadius($r('app.float.ds_radius_md')).padding({left:$r('app.float.ds_space_md'),right:$r('app.float.ds_space_md'),top:$r('app.float.ds_space_sm'),bottom:$r('app.float.ds_space_sm')})

卡片:

Column({space:8}).padding($r('app.float.ds_space_lg')).backgroundColor($r('app.color.ds_bg_surface')).borderRadius($r('app.float.ds_radius_lg')).border({width:1,color:$r('app.color.ds_border_default')})

标签(品牌/成功/警告/危险四色):

Text('成功').fontColor($r('app.color.ds_success')).backgroundColor($r('app.color.ds_success_container')).borderRadius($r('app.float.ds_radius_full'))

语义色 + 容器色配对,是设计系统的标准做法:文字用高饱和色,背景用低饱和同色系。

6.3 色板数据驱动

Demo 将 Token 组织为可遍历数组,便于文档化展示:

interfaceColorSwatch{id:stringtoken:stringlabel:stringcolor:ResourceColor}constBRAND_SWATCHES:ColorSwatch[]=[{id:'c1',token:'ds_brand_primary',label:'主色',color:$r('app.color.ds_brand_primary')}]

$r()返回值可直接存入常量数组,ForEach 渲染色板——这也是团队内部 Token 文档页的常见实现。


七、暗色模式:零代码适配

7.1 原理

组件引用 $r('app.color.ds_bg_page') │ ▼ 系统判断当前主题 ╱ ╲ base/color.json dark/color.json #F9FAFB #111827

组件层不出现任何isDarkMode判断。资源框架在运行时解析正确的值。

7.2 开发注意事项

  1. 所有颜色必须走 Token— 一个#FFFFFF漏网就会暗色下刺眼
  2. 暗色值不是简单反转— 品牌色提亮、边框色减淡,需设计稿校准
  3. 对比度—ds_text_primary与ds_bg_surface在两端都需满足 WCAG AA(对比度 ≥ 4.5:1)
  4. 测试方法— 系统设置切换暗色,或在 DevEco 预览器选择 dark 主题

7.3 容器色的暗色处理

亮色:ds_success_container = #ECFDF5(浅绿底)
暗色:ds_success_container = #064E3B(深绿底)

容器色在暗色模式下反而加深,避免大面积浅色刺眼,同时保持语义色相。


八、团队落地工作流

8.1 设计 → 开发协作

Figma Design Tokens │ ▼ 导出/对照 resources/color.json + float.json │ ▼ 引用 $r('app.color.ds_*') 全 App 消费 │ ▼ 变更 只改 JSON → 全 App 生效

建议在设计稿中直接使用与代码一致的 Token 名(ds_text_primary),减少翻译损耗。

8.2 渐进式迁移策略

不必一次性重构全 App。推荐顺序:

阶段范围做法
Phase 1新页面强制使用 Token,禁止硬编码
Phase 2核心组件Button、Card、Input 封装 Token
Phase 3存量页面按模块逐步替换#hex→$r()
Phase 4审查CI 脚本扫描硬编码色值

8.3 与 @Styles / @Extend 结合

对于重复样式组合,可封装:

@Extend(Text)functiondsBody1(){.fontSize($r('app.float.ds_font_body1')).fontColor($r('app.color.ds_text_primary'))}@Extend(Column)functiondsCard(){.padding($r('app.float.ds_space_lg')).backgroundColor($r('app.color.ds_bg_surface')).borderRadius($r('app.float.ds_radius_lg')).border({width:1,color:$r('app.color.ds_border_default')})}// 使用Text('正文').dsBody1()Column().dsCard()

Token 定义「值」,@Extend 定义「组合」—— 两层分工,避免每个组件重复写五六个属性。

8.4 Code Review 检查项

  • 新增颜色是否定义为 Token?
  • 组件是否使用$r()而非 hex?
  • 暗色color.json是否有对应覆写?
  • 间距是否为 4vp 网格倍数?
  • 命名是否符合ds_<category>_<role>规范?

九、常见误区

9.1 「Token 就是常量类」

DSColor.brandPrimary = '#2563EB'在 TypeScript 里编译期固定,无法响应系统主题切换。resources Token 才是运行时主题感知的正解。常量类可作为补充,不应替代 resources。

9.2 「暗色模式在组件里判断」

// 不推荐.fontColor(this.isDark?'#F9FAFB':'#111827')// 推荐.fontColor($r('app.color.ds_text_primary'))

前者每处都要写判断,后者集中管理。

9.3 「Token 名用色值描述」

ds_blue_500在品牌换色后名不副实。用语义名ds_brand_primary,值可以随便换。

9.4 「间距随意定」

padding(15)、margin(13)破坏网格节奏。Stick to the scale:xs/sm/md/lg/xl/xxl。

9.5 「只定义亮色」

暗色适配不是可选项。从第一天就在dark/color.json维护同名 Token,成本远低于事后补。


十、Token 规模参考

Demo 当前规模:

类型数量文件
颜色17color.json × 2
间距6float.json
圆角5float.json
字阶6float.json
合计34—

一个中等复杂度 App,颜色 Token 通常在 20–40 个,间距/圆角/字号各 5–8 个。不宜贪多——每个 Token 都应有明确使用场景,否则团队记不住。

扩展时优先加语义色(如ds_info、ds_link),而非加第 17 个灰色变体。





十一、总结

设计系统 Token 变量库的本质,是把视觉决策从组件代码中抽离到 resources:

定义一次 → 处处引用 → 改一处全局生效

HarmonyOS 落地三件套:

文件放什么引用方式
base/element/color.json语义化颜色$r('app.color.ds_*')
dark/element/color.json暗色覆写同名,自动切换
base/element/float.json间距/圆角/字号$r('app.float.ds_*')

关键原则:

  1. 语义命名—ds_text_primary,不是ds_gray_900
  2. 4vp 网格— 间距刻度可预期
  3. 零硬编码— 组件不出现#hex魔法数字
  4. 双目录主题— base + dark 同名 Token
  5. 渐进迁移— 新代码强制 Token,存量逐步替换

本文 Demo 在四个场景中完整展示了色板、间距刻度、圆角阶梯和组件画廊。建议你在真机上运行后,到系统设置切换暗色模式——观察ds_bg_page从浅灰变为深蓝灰,而组件代码一行未改。这就是 Token 变量库的价值。

设计系统不是设计师的文档摆设,而是开发团队的视觉基础设施。从第一个ds_brand_primary开始,你的 App 就拥有了统一的语言。


相关文件:

  • Demo 页面:entry/src/main/ets/pages/Index.ets
  • 亮色 Token:entry/src/main/resources/base/element/color.json、float.json
  • 暗色 Token:entry/src/main/resources/dark/element/color.json

编译命令:

node/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js assembleHap-pproduct=default-pbuildMode=debug --no-daemon

相关新闻

  • 【小白也能轻松玩转龙虾】虾壳云一键部署超简单,新手快速完成 OpenClaw v2.7.9 全套安装(附最新安装包)
  • 【Unity面试篇】Unity 进阶面试题深度剖析与高频考点精讲 | ❤️持续更新❤️
  • Axure RP中文语言包终极指南:10分钟轻松实现界面中文化 [特殊字符]

最新新闻

  • 多轮 Agent 会话爆雷?7 套工程化解决方案,从 90% 幻觉到 97% 可用!
  • 南城公司注册|东莞南城初创企业工商代办合规指南 - GrowUME
  • Windows C盘空间清理与系统优化实战指南
  • 2026年AI降本增效工具平台核心技术解析
  • # 粉笔省考「不够本地」?8 个真实问法,把优势一次说清楚
  • [NLP]LLM 训练显存需求拆解:从理论公式到实战配置

日新闻

  • AWS SSM安全运维实践:零公网暴露的合规远程开发方案
  • Tableau 2024.1 图表选择指南:5种业务场景与最佳图表类型匹配
  • dsPIC33FJ与CMT-8540S-SMT在嵌入式音频处理中的高效应用

周新闻

  • IX9104 PCIe5.0 高速交换芯片@ACP#完整规格 + 应用场景总结
  • Unity游戏集成Coze智能体:实现NPC智能对话与知识库联动
  • SAP EPIC 建行回单查询:从标准类CL_EPIC_EXAMPLE_CN_CCB_GHTD到Z类的5处关键修改

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号