ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【细胞工坊|17】HarmonyOS ArkTS 视觉令牌实战:集中深色主题、间距和交互状态避免页面割裂

【细胞工坊|17】HarmonyOS ArkTS 视觉令牌实战:集中深色主题、间距和交互状态避免页面割裂 本文基于「细胞工坊」真实 HarmonyOS ArkTS 源码编写源码目录为D:\huawei\one14-9重点复核entry/src/main/ets/common/Theme.ets、Constants.ets、Index.ets以及首页、实验室、学习、我的等页面中的令牌调用。需要先说明一个边界当前源码已经把深色视觉系统集中到AppColors、AppFonts和Constants中但没有实现亮色主题切换、资源限定目录、WithTheme或运行时setColorMode。因此本文讨论的是 HarmonyOS 5.0 ArkUI 项目中“把深色主题令牌先收口”的工程方法不会把源码没有实现的亮暗色动态切换写成已完成能力。一、为什么先做视觉令牌而不是直接谈亮暗色切换HarmonyOS 应用做深浅色适配时真正难的不是调用某个 API而是先让页面不再各写各的颜色、字号、圆角和状态色。如果项目里每个页面都直接写#FFFFFF、#111827、16、12后续要改主题时就会出现三个问题第一无法确认哪个颜色是背景、哪个颜色是正文、哪个颜色只是临时装饰第二选中态、未选中态、提示态和危险态容易在不同页面表达不一致第三发布前做 AppGallery 色彩对比、深色模式、状态栏和导航栏检查时需要跨页面逐个猜测意图。「细胞工坊」的源码没有直接完成亮暗色双主题但它已经做了一个更基础也更必要的工程动作把视觉变量集中到了Theme.ets。AppColors负责颜色语义AppFonts负责字号和字重Constants负责页面边距、卡片间距、圆角以及 Tab 索引。这个设计在真实项目里很关键因为它把“页面长什么样”从页面实现中抽出来变成可搜索、可审计、可替换的公共契约。以TAB_SELECTED为例它不是一个孤立颜色而是底部导航选中态的语义名称。页面不需要知道选中态究竟是绿色、蓝色还是品牌色只需要消费AppColors.TAB_SELECTED。当后续要把深色主题扩展成亮色主题时至少可以先定位到哪些地方依赖这个语义再决定是否把它升级为资源引用、系统色或主题映射。二、源码里的主题入口Theme.ets 的职责划分Theme.ets里有两个核心类AppColors和AppFonts。AppColors把颜色拆成几组品牌主色、强调色、页面结构色、文字层级色、Tab 状态色和生物实验分类色。它没有把变量命名成BLUE_1、GREEN_2这种纯视觉名称而是用了PAGE_BG、CARD_BG、TEXT_PRIMARY、TAB_UNSELECTED、CAT_DNA这类更接近业务和 UI 语义的名字。export class AppColors { static readonly PAGE_BG: string #0B1120 static readonly CARD_BG: string #111827 static readonly DIVIDER: string #1F2937 static readonly TEXT_PRIMARY: string #E5F7FF static readonly TEXT_SECONDARY: string #A7B6C8 static readonly TEXT_HINT: string #94A3B8 static readonly TAB_UNSELECTED: string #94A3B8 static readonly TAB_SELECTED: string #00FFB2 }这种命名方式的价值在于它能让开发者从调用点反推设计意图。比如PAGE_BG出现在页面根容器时读者可以知道它是页面背景CARD_BG出现在卡片区域时可以知道它是信息容器背景TEXT_SECONDARY出现在副标题和说明文字时可以知道它是二级信息ACCENT_RED出现在风险、错误、病毒类信息时可以知道它承担提醒或危险含义。AppFonts的职责更窄当前集中定义了TITLE_SIZE、SUBTITLE_SIZE、BODY_SIZE、CAPTION_SIZE以及WEIGHT_BOLD、WEIGHT_MEDIUM、WEIGHT_NORMAL。这不是复杂排版系统但它足够支撑一个教育工具类应用的基础层级。标题、正文、说明、角标如果都从这里取值页面之间不会因为临时写了 13、15、17 这类数字而变得割裂。export class AppFonts { static readonly TITLE_SIZE: number 20 static readonly SUBTITLE_SIZE: number 16 static readonly BODY_SIZE: number 14 static readonly CAPTION_SIZE: number 12 static readonly WEIGHT_BOLD: FontWeight FontWeight.Bold static readonly WEIGHT_MEDIUM: FontWeight FontWeight.Medium static readonly WEIGHT_NORMAL: FontWeight FontWeight.Normal }这里也能看出源码的边界它没有把颜色定义成 HarmonyOS 资源$r(app.color.xxx)也没有提供 light/dark 两套资源文件。也就是说它当前更像一个 ArkTS 层的深色主题 token 表而不是完整的系统主题适配方案。这个边界需要在文章和发布材料里写清楚否则就是夸大源码能力。三、Constants.ets把间距和圆角也纳入视觉契约很多项目把“主题”只理解成颜色但页面割裂往往来自间距和形状。一个页面卡片圆角 16另一个页面圆角 20一个页面左右边距 12另一个页面左右边距 24列表间距、底部导航高度、卡片内边距各自手写最终会让用户感觉像在切换不同应用。「细胞工坊」在Constants.ets中定义了PAGE_PADDING、CARD_GAP、CARD_RADIUS、SMALL_RADIUS并定义了TAB_HOME、TAB_LAB、TAB_LEARN、TAB_MINE。前一组是视觉间距令牌后一组是导航状态令牌。它们虽然不是颜色但和视觉稳定性直接相关。PAGE_PADDING 16让页面内容有统一的呼吸空间CARD_GAP 12让卡片之间形成稳定节奏CARD_RADIUS 16和SMALL_RADIUS 12则把大卡片、小标签、按钮容器的圆角层级分开。对 HarmonyOS 多设备页面来说这类令牌尤其重要。折叠屏、平板、2in1 小窗下布局宽度会变化但统一间距和圆角能减少页面在不同尺寸上的视觉漂移。export class Constants { static readonly PAGE_PADDING: number 16 static readonly CARD_GAP: number 12 static readonly CARD_RADIUS: number 16 static readonly SMALL_RADIUS: number 12 static readonly TAB_HOME: number 0 static readonly TAB_LAB: number 1 static readonly TAB_LEARN: number 2 static readonly TAB_MINE: number 3 }Tab 索引被放进Constants也有工程意义。底部导航如果用裸数字0、1、2、3页面读起来不直观后续增加 Tab 或调整顺序时容易漏改。用Constants.TAB_HOME这类语义值状态判断和页面渲染之间的关系更清楚。四、Index.ets底部导航如何消费 TAB_SELECTEDIndex.ets是观察令牌是否真正落地的关键文件。页面的根容器背景使用AppColors.PAGE_BG底部导航背景使用AppColors.CARD_BG分割线使用AppColors.DIVIDERTab 图标和文字通过当前索引判断使用AppColors.TAB_SELECTED或AppColors.TAB_UNSELECTED。这说明令牌不是停留在公共文件里而是进入了主导航状态机。.fontColor(this.currentTab index ? AppColors.TAB_SELECTED : AppColors.TAB_UNSELECTED)底部导航的状态逻辑可以概括成一个小闭环用户点击 TabcurrentTab更新TabBarBuilder根据currentTab index判断是否选中选中态使用TAB_SELECTED未选中态使用TAB_UNSELECTED页面内容根据currentTab切换到首页、实验室、学习或我的。这里的颜色并不是装饰而是状态反馈的一部分。这类写法对 AppGallery 审核也有帮助。发布前检查底部导航时至少可以快速确认选中态和未选中态来自同一组令牌而不是某个页面自己写了一套颜色。如果未来审查发现绿色在深色背景上对比度不够只需要优先检查TAB_SELECTED和底部导航背景而不是在所有页面中搜索每个图标颜色。Index.ets还使用了StorageProp(statusBarHeight)和StorageProp(bottomBarHeight)处理顶部状态栏和底部安全区这和视觉令牌配合起来形成了两个层面的稳定性颜色和状态由令牌控制安全区由系统高度属性控制。对于 HarmonyOS 5.0 设备这比单纯写固定 padding 更适合多设备和系统栏场景。五、HomePage主题令牌让首页信息层级更明确首页通常最容易堆样式因为它同时承担欢迎语、推荐内容、统计入口、快速操作和功能导览。「细胞工坊」的HomePage.ets中大量使用了AppColors.PAGE_BG、AppColors.CARD_BG、AppColors.TEXT_PRIMARY、AppColors.TEXT_SECONDARY、AppColors.TEXT_HINT、AppFonts.TITLE_SIZE、AppFonts.BODY_SIZE以及Constants.PAGE_PADDING、Constants.CARD_RADIUS。.backgroundColor(AppColors.PAGE_BG) .padding(Constants.PAGE_PADDING) .backgroundColor(AppColors.CARD_BG) .borderRadius(Constants.CARD_RADIUS)这些调用让首页的信息层级有了可解释的规则。标题使用主文字和标题字号说明使用二级文字或提示文字卡片使用统一背景页面外层使用统一深色背景卡片圆角和页面边距来自统一常量。用户看到的不是某一段代码的样式堆砌而是一套重复出现的视觉秩序。首页仍然存在一些硬编码颜色比如渐变背景、按钮或标签上的临时透明色。这些不是不能存在但必须被看作后续主题适配的风险点。更稳妥的做法是把这些颜色继续拆成语义令牌例如HERO_GRADIENT_START、HERO_GRADIENT_END、CHIP_BG_ACTIVE、CHIP_BORDER。这样在亮色主题或高对比模式下开发者不会遗漏它们。这也是本文刻意不把当前源码写成“已完成亮暗色适配”的原因。真实源码里已经有主题集中化基础但仍有硬编码颜色没有完全收口。把这一点写清楚比泛泛声称“支持深浅色模式”更有技术可信度。六、LabPage分类色要有业务语义不能只靠好看LabPage.ets负责实验相关页面里面会出现生物实验分类、卡片列表、实验说明和状态入口。Theme.ets中的CAT_BASIC、CAT_CULTURE、CAT_DNA、CAT_VIRUS正好对应这类业务分类。与普通品牌色相比分类色更容易被滥用因为开发者可能只按“哪个颜色好看”来选择而忽略颜色和内容类型之间的稳定关系。把分类色放在AppColors里至少带来两个好处。第一分类颜色有了命名入口后续可以在模型、页面和设计稿之间保持一致。第二发布前可以统一检查分类色与文字、标签背景之间的对比度。尤其是CAT_DNA这类紫色、CAT_VIRUS这类红色在深色背景上可能很醒目但如果放到浅色背景、半透明卡片或小字号标签里就需要重新验证。LabPage中同样可以看到CARD_BG、TEXT_PRIMARY、TEXT_SECONDARY、DIVIDER等令牌的使用。它说明主题系统不是只服务首页而是在实验功能页继续复用。对一个合集文章来说这一点很重要文章不是抽象讨论设计规范而是能回到源码中看到同一套令牌在多个页面上被消费。仍需注意的是页面里如果继续存在临时边框色、半透明背景色或渐变色后续要么补充语义令牌要么在发布前做单独检查。分类色只是主题系统的一部分不能替代完整的前景/背景/边框/状态色体系。七、LearningPage 与 MinePage统一文字层级比统一颜色更容易被低估学习页和我的页通常包含大量说明文字、入口卡片、统计数字和二级信息。用户在这些页面中不是只看颜色而是在快速判断“什么最重要、什么可以稍后看、什么是状态说明”。如果字号和字重没有统一规则即使颜色统一了页面仍然会显得杂乱。AppFonts.TITLE_SIZE、SUBTITLE_SIZE、BODY_SIZE、CAPTION_SIZE在这里发挥作用。标题、模块名、正文说明、角标说明之间形成固定层级开发者新增模块时不需要重新决定每个文本大小。对 ArkUI 页面来说这也减少了Text()链式调用里的魔法数字让 UI 树更可读。从维护角度看字体令牌还有一个现实价值AppGallery 和多设备布局检查会关注文字是否过小、截断、遮挡、不可读。如果字号散落在页面里很难系统检查。如果集中在AppFonts中至少可以先判断基础字号是否符合手机、平板和 2in1 的可读性要求再针对少量特殊场景做例外处理。当然当前源码里的AppFonts还比较轻量没有定义行高、字间距、数字字体、标题层级 H1/H2/H3也没有针对 PC 或平板单独给字号策略。这不是缺陷而是项目阶段的边界。对一个教育类工具应用来说先把常用字号和字重集中起来已经能避免大量页面割裂问题。八、从深色主题令牌走向完整亮暗色适配需要补哪几层如果后续要把「细胞工坊」从当前深色令牌系统升级为完整亮暗色适配不能只把PAGE_BG改成白色。完整方案至少要补四层。第一层是资源层。更符合 HarmonyOS 工程习惯的做法是把关键颜色迁移为资源例如app.color.page_bg、app.color.card_bg、app.color.text_primary再通过不同资源目录或主题机制提供深色和亮色值。这样系统色彩模式变化时页面可以通过资源解析获得不同结果而不是在 ArkTS 类里硬判断。第二层是语义层。当前AppColors已经有语义命名基础但还需要把所有散落的硬编码颜色纳入语义命名。尤其是渐变、边框、半透明遮罩、按钮背景、标签背景、阴影和危险状态不能只集中正文颜色。很多深浅色问题恰恰出现在这些“不重要”的颜色上。第三层是组件层。页面不应该每次都从零组合卡片、标签、按钮和统计块。可以把高频组件抽成AppCard、SectionHeader、CategoryChip、StatTile让组件内部消费令牌。这样主题变化时组件级调整能覆盖更多页面。第四层是验证层。深色和亮色都要检查正文对比度、按钮状态、禁用态、选中态、状态栏、导航栏、底部安全区、弹窗和长文本。尤其是TAB_SELECTED这类状态色要在深色导航背景和可能的亮色导航背景上分别验证不然选中态可能只在一种模式下清楚。九、发布前如何审计视觉令牌是否真正可复核写技术文章和做发布材料时不能只说“使用主题令牌”。需要能被源码复核。对 05-17 这篇文章我会按下面的方式审计。第一检查Theme.ets是否真实存在AppColors和AppFonts并列出关键字段例如PAGE_BG、CARD_BG、TEXT_PRIMARY、TEXT_SECONDARY、TAB_SELECTED。这些字段就是文章中的可复核证据。第二检查Constants.ets是否真实存在页面边距、卡片间距、圆角和 Tab 索引。文章中关于间距和导航状态的讨论必须能回到这些常量。第三检查Index.ets是否真实使用TAB_SELECTED和TAB_UNSELECTED。如果文章强调底部导航状态色就必须能从主入口页面找到调用点。第四检查首页、实验室、学习和我的页面是否真实消费AppColors、AppFonts或Constants。如果某个页面完全没有使用令牌就不能把它写成主题系统的落地点。第五记录边界。当前源码没有实现完整亮色主题切换因此文章标题和正文都要避免“已完成亮暗色适配”这种表述。更准确的说法是源码完成了深色视觉令牌集中化为后续亮暗色适配打基础。十、工程落地建议把硬编码颜色变成下一轮重构清单对这类项目我不建议一次性把所有页面重构成复杂设计系统。更务实的路径是先保留当前AppColors、AppFonts、Constants然后建立一张硬编码颜色清单。每发现一个页面内直接写颜色就判断它属于哪种语义页面背景、卡片背景、边框、正文、说明、提示、成功、危险、分类、选中、禁用、渐变或遮罩。能归入现有令牌的直接替换例如文本颜色、卡片背景和分割线。不能归入现有令牌的新增语义字段而不是用COLOR_1、COLOR_2这种无意义名字。比如首页渐变可以拆成HOME_HERO_GRADIENT_START和HOME_HERO_GRADIENT_END分类标签可以拆成CATEGORY_CHIP_BG和CATEGORY_CHIP_BORDER。同时组件抽取要跟着真实重复走。页面里只出现一次的样式没必要马上抽组件跨首页、实验室、学习页重复出现的卡片、标题行、统计块才值得抽。否则主题系统会过早变成一组难以理解的抽象反而降低维护效率。最后任何主题重构都要配合截图和实际设备检查。视觉令牌不是为了让代码好看而是为了让用户在不同页面、不同设备、不同系统栏环境中得到一致体验。对于 HarmonyOS 5.0 应用这一点直接关系到多设备适配和 AppGallery 审核风险。十一、本文对应的真实结论基于D:\huawei\one14-9的源码05-17 这篇文章能成立的真实结论是细胞工坊已经把深色主题中的核心颜色、字体层级、页面间距、卡片圆角和底部导航状态集中到公共文件Index.ets、HomePage.ets、LabPage.ets、LearningPage.ets等页面实际消费了这些令牌TAB_SELECTED是底部导航选中态的明确标记当前源码仍有部分硬编码颜色需要作为后续亮暗色适配和审核前视觉检查的风险点处理。不能成立的结论是源码已经支持完整亮色主题、系统深浅色自动切换、WithTheme局部主题覆盖或资源限定目录。文章不会把这些能力写成已实现。真正可靠的技术写作应该把“已经做了什么”和“下一步可以怎么做”分开。对细胞工坊来说当前已经完成的是视觉令牌收口下一步才是把这些令牌迁移到更完整的 HarmonyOS 主题资源体系并补齐亮色模式、组件化和对比度验证。部分内容由AI辅助生成。
返回列表