【OpenHarmony/HarmonyOs 】ArkUI 设计系统入门:用 UiTokens 统一颜色、间距、圆角与阴影

前言

当页面从一个增长到六个时,直接写颜色和尺寸会迅速失控:同样的灰色出现多个值,同级卡片圆角不同,修改品牌色需要全项目搜索。LinkOS 链界建立了一个轻量 UiTokens,把视觉常量集中管理。本文介绍这种做法,并进一步讨论深色模式、资源系统和多设备响应式布局。🎨

一、什么是 Design Token

Design Token 是设计决策的代码化表达。它不描述“某个按钮宽 44”,而描述“中号圆角”“主色”“弱文本色”等可复用语义。

export class UiTokens {
  static readonly COLOR_PRIMARY = '#0A59F7';
  static readonly COLOR_BG = '#F7F8FA';
  static readonly COLOR_CARD = '#FFFFFF';
  static readonly COLOR_TEXT = '#0B1220';
  static readonly COLOR_TEXT_MUTED = '#6B7280';
  static readonly COLOR_BORDER = '#EEF0F4';

  static readonly RADIUS_SM = 10;
  static readonly RADIUS_MD = 14;
  static readonly RADIUS_LG = 18;
  static readonly RADIUS_XL = 24;

  static readonly SPACE_8 = 8;
  static readonly SPACE_12 = 12;
  static readonly SPACE_16 = 16;
  static readonly SPACE_24 = 24;
}

页面使用语义常量后,设计语言更统一:

Column() { /* 卡片内容 */ }
  .padding(UiTokens.SPACE_16)
  .backgroundColor(UiTokens.COLOR_CARD)
  .borderRadius(UiTokens.RADIUS_LG)
  .border({ width: 1, color: UiTokens.COLOR_BORDER })
  .shadow(UiTokens.SHADOW_SM)

二、阴影也需要结构化

export interface ShadowToken {
  radius: number;
  color: string;
  offsetX?: number;
  offsetY?: number;
}

static readonly SHADOW_SM: ShadowToken = {
  radius: 6,
  color: 'rgba(15, 23, 42, 0.06)',
  offsetY: 2
};

把阴影作为一个整体 Token,可以保证 radius、透明度和偏移不会被随意组合。建议只保留少量阴影层级:普通卡片、浮层、模态框。层级越多,界面越容易显得杂乱。

三、Builder 是轻量组件化工具

页面中反复出现标签、统计卡、导航项,可以使用 @Builder

@Builder
StatsCard(title: string, value: string) {
  Column() {
    Text(value)
      .fontSize(18)
      .fontWeight(FontWeight.Bold)
    Text(title)
      .fontSize(11)
      .fontColor(UiTokens.COLOR_TEXT_MUTED)
  }
  .height(60)
  .backgroundColor(UiTokens.COLOR_CARD)
  .borderRadius(UiTokens.RADIUS_LG)
}

Builder 适合同一页面内部的重复结构。若组件跨页面复用、拥有独立状态或生命周期,应提取为独立 @Component 文件。例如当前四个主页面都实现了类似底部导航,后续最好统一为公共组件,避免修改一处却遗漏其他页面。

四、两列 Grid 只是响应式起点

欢迎页和元服务页使用:

Grid() { /* items */ }
  .columnsTemplate('1fr 1fr')
  .columnsGap(12)
  .rowsGap(12)

两列在手机上合适,但项目声明支持 phone、tablet、2in1,固定两列会浪费宽屏空间。响应式设计应根据窗口宽度切换布局:

< 600vp       2 列,底部导航
600-840vp     3 列,内容区适度加宽
> 840vp       4 列或侧边导航,限制正文最大宽度

宽屏适配不是把所有元素同比放大。正确做法通常是增加列数、提高信息密度、限制内容最大宽度,并让导航位置更符合大屏操作。

五、安全区与底部导航

底部导航调用:

.expandSafeArea(
  [SafeAreaType.SYSTEM],
  [SafeAreaEdge.BOTTOM]
)

这能处理手势导航区域与设备底部差异。欢迎页同时扩展顶部和底部背景,使渐变铺满屏幕,但内容仍应保留足够 padding。背景扩展和可交互内容避让是两个不同概念。

六、深色模式不能只准备一个资源目录

项目已有 resources/dark/element/color.json,但不少页面仍直接写十六进制颜色。硬编码颜色不会自动响应深色资源。

要真正支持深色模式,应逐步将语义颜色迁移为资源:

.backgroundColor($r('app.color.page_background'))
.fontColor($r('app.color.text_primary'))

系统切换配置后,资源系统会选择对应限定目录。品牌渐变、插图等少量特殊颜色可以保留 Token,但文本、背景、边框等基础语义最好资源化。

七、文字与本地化

直接写中文便于原型开发,但语言切换仅保存 zh-CN/en-US 并不会自动改变页面文本。完整国际化需要:

  1. 把字符串放入 resources/base/element/string.json
  2. 增加语言限定资源;
  3. 页面通过 $r('app.string.xxx') 引用;
  4. 切换语言时更新应用配置或重新加载相关页面;
  5. 检查英文变长后卡片和按钮是否溢出。

八、视觉一致性检查清单

  • 同层级标题是否使用相同字号与字重;
  • 卡片是否只使用有限的圆角层级;
  • 文本和背景对比度是否足够;
  • 所有点击目标是否有稳定尺寸;
  • 长标题是否设置 maxLines 与省略;
  • 手机、平板、横屏是否没有重叠;
  • 深色模式下边框和阴影是否仍然自然;
  • 动画和颜色是否照顾减少动态效果与色觉差异用户。

九、总结

设计系统不一定从庞大的组件库开始。先统一颜色、间距、圆角和阴影,再用 Builder 消除页面内重复,用公共组件解决跨页面一致性,最后结合资源限定目录实现深色与国际化,就能让 ArkUI 项目从“每页各写各的”逐步走向稳定的产品界面。✅

img

img

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐