当前位置: 首页 > news >正文

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


文章目录

    • 前言
    • 一、为什么需要 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,暗色值

系统根据当前主题自动选择basedark目录下的同名资源。组件始终引用$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 文件名(colorfloatstring)。

2.3 单位约定

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

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


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

3.1 推荐格式

ds_<category>_<role>
部分含义示例
ds_设计系统前缀,避免与业务资源冲突
category类别textbgspaceradius
role语义角色primarysecondarymdlg

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_primaryds_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_infods_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.jsonfloat.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
http://www.jsqmd.com/news/1188970/

相关文章:

  • 深入解析C++ STL:从泛型编程原理到高性能容器实战
  • 2026便携式汽车轮重仪品牌排行榜:浙江润鑫以精准数据领跑赛道,稳居行业前列 - 品牌速递
  • AD7175-8与PIC24FV32KA302构建高精度信号采集系统
  • 【小白也能轻松玩转龙虾】虾壳云一键部署超简单,新手快速完成 OpenClaw v2.7.9 全套安装(附最新安装包)
  • 多模态RAG在医疗影像报告生成中的应用与优化
  • 【Unity面试篇】Unity 进阶面试题深度剖析与高频考点精讲 | ❤️持续更新❤️
  • 终极指南:3分钟免费解锁Microsoft 365完整功能的神奇方法
  • Axure RP中文语言包终极指南:10分钟轻松实现界面中文化 [特殊字符]
  • 2026成都锦江回收爱马仕Birkin,闲置包包上门估价当场打款 - 逸程奢侈品回收中心
  • 2026年7月最新扬州劳力士官方售后服务热线与网点地址查询 - 劳力士官方服务中心
  • AI对话前端从入门到崩溃:一个长对话引发的五层优化战争【四】
  • 别再只盯着Java八股文!大模型时代,测试岗面试题已经彻底变了
  • CPM4OSSP-PROXY安全防护:构建企业级软件包管理安全体系
  • 事后复盘文化:SRE 如何通过无责复盘从失败中学习
  • AI+IoT城市噪音治理:声学传感网络+AI降噪+智能执法
  • 深圳钻石变现常见误区汇总!新手卖钻高频踩坑问题深度解答 - 奢侈品回收测评
  • 合肥黄金变现避坑指南,5家小区周边中检认证回收门店一览,就近称重金价透明 - 逸程奢侈品回收中心
  • 20262026年7月广州中央空调回收价格表 陆浩上门免费估价报价 - 广东再生资源回收
  • 对照欧洲的车载DAB测试,CDR和DRM要补哪几课
  • 如何用OpenCore Legacy Patcher让旧Mac重获新生:5步完整指南
  • 大模型基本概念和原理
  • 卷积神经网络(CNN)原理详解:从视觉机制到深度学习实战
  • 2026超限运输检测仪品牌排行,浙江润鑫凭高性价比脱颖而出,成行业标杆 - 品牌速递
  • YOLOv8在寄生虫识别中的应用:从数据标注到系统部署全流程
  • 杭州全域线下黄金回收网点,东西片区均有门店可随时到访 - 小蝶回收测评
  • 割接当晚,10ms慢查询狂飙45秒!我靠4板斧“扒光”金仓CBO底裤,让MySQL迁移性能反超300%
  • **青岛LV包包回收,2026年7月最新参考** - 生活时报
  • 高精度信号采集系统设计:AD7175-8与PIC18F4585应用指南
  • 广州街坊实测|2026首饰回收真实行情8家正规门店避坑攻略 - 禹竞奢收行
  • python数据可视化技巧的100个练习 -- 12. 创建年龄分布的小提琴图