【口算王|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
【口算王|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂
证据边界:本文依据 D:/huawei/one16-11 当前可读取源码与资源文件整理;本轮未执行构建、模拟器、真机、系统暗色切换、字体缩放或 AppGallery 审核。迁移路线属于建议实现,不代表双主题已完成。

视觉一致性不是“所有页面都用同一个蓝色”。一个可维护的主题系统需要回答:背景和卡片是什么关系,主文字与提示文字如何分级,收藏、正确、错误和警告分别表达什么语义,字号、圆角、间距和触控尺寸由谁统一管理,以及系统切到暗色时应用究竟跟随还是保持浅色。
口算王当前选择了明确的浅色策略:EntryAbility 调用 setColorMode(COLOR_MODE_LIGHT),页面主要消费 librarya 导出的 Colors 和 Sizes。蓝色作为主操作色,橙色作为强调色,背景、表面、文字、分割线和答题状态都有集中常量。但源码也存在三类边界:base 与 dark 的资源颜色完全相同且仍是旧绿色方案;ArkTS 页面没有使用 app.color.*;entry 与 librarya 各有一份 ThemeConstants,另有 15 个文件仍包含硬编码色值。
本文基于口算王项目 D:\huawei\one16-11 的真实源码,复核 EntryAbility.ets、entry 与 librarya 中的 ThemeConstants.ets、base/dark color.json、PracticePage.ets、HomePage.ets、FavoritePage.ets、SearchPage.ets、SettingsPage.ets 与共享组件。包名 com.jiaweikang.one16 是本文草稿核验使用的唯一标记。项目目标和兼容 SDK 均为 HarmonyOS 6.0 系列,本文方法适用于 HarmonyOS 5.0 及以上 ArkUI 应用。
本文将完成六件事:
- 区分“锁定浅色”和“完成亮暗色适配”;
- 梳理品牌、背景、文字和状态令牌;
- 分析字号、间距、圆角与触控尺寸的统一作用;
- 复核答题选中、正确、错误等真实交互状态;
- 找出资源颜色、重复常量和硬编码颜色的漂移风险;
- 给出从浅色锁定迁移到资源化双主题的实施路径。
一、先明确当前真实主题策略
应用启动时执行:
)
这表示应用主动锁定浅色模式。系统切换到暗色时,应用仍应保持浅色视觉,不是自动解析 dark 资源。
因此,当前能力应准确描述为:
- 已实现浅色模式锁定;
- 已建立浅色视觉令牌;
- 已提供 dark 资源目录,但内容未形成暗色配色;
- 未实现用户主题开关;
- 未实现跟随系统的动态双主题。
把 dark 目录存在等同于暗色适配完成,会造成能力夸大。
二、为什么锁定浅色也需要验证系统暗色
锁定浅色并不意味着可以跳过暗色环境测试。系统状态栏、导航栏、启动窗口和某些系统组件仍可能受系统模式影响。必须在设备开启暗色时检查:
- 页面背景是否仍是预期浅色;
- 状态栏图标是否与浅背景有足够对比;
- 启动窗口是否闪现深色;
- 系统弹窗和应用自定义弹窗衔接是否突兀;
- 图片、图标是否被错误反色;
- Web 或第三方组件是否自行跟随系统。
当前源码主动设置浅色,是一个产品选择,也是一项需要全链路验证的发布约束。
三、颜色令牌按语义分层
librarya 的 Colors 不是平铺色板,而是按角色组织。
品牌层
PRIMARY_LIGHT = '#EAF1FF'
PRIMARY 用于主按钮、选中、链接和重要图标;PRIMARY_DARK 用于渐变深端;PRIMARY_LIGHT 用于浅底标签和选中背景;ACCENT 提供第二强调色。
表面层
SURFACE = '#FFFFFF'
BACKGROUND 是页面底色,SURFACE 是卡片和操作栏,BACKGROUND_ALT 用于提示、说明和局部强调区。
内容层
TEXT_HINT = '#6A7388'
三级文本不是通过随意调透明度产生,而是使用稳定色值表达标题、正文和提示层级。
状态层
WARNING = '#8A5A00'
状态色承担业务语义,不能只当装饰色使用。
四、浅色主题不是单一蓝色
口算王同时使用蓝、橙、绿、红和暖黄色背景。蓝色负责主路径,橙色负责强调,绿色表达正确与成功,红色表达错误和破坏性操作,暖黄色承担提示。
这种一主多语义配色避免整个应用只剩蓝色深浅变化。不同颜色的使用规则比色值本身更重要:
- 主操作统一蓝色;
- 次级强调优先橙色;
- 正确只使用 SUCCESS;
- 错误、删除和高风险只使用 ERROR;
- 提示文字不使用状态色;
- 浅色背景必须搭配足够深的文字或边框。
五、答题选项拥有完整状态令牌
PracticePage 的答案选项使用专门令牌:
| 状态 | 背景 | 边框 |
|---|---|---|
| 默认 | OPTION_BG | OPTION_BORDER |
| 已选 | OPTION_SELECTED_BG | OPTION_SELECTED_BORDER |
| 正确 | OPTION_CORRECT_BG | OPTION_CORRECT_BORDER |
| 错误 | OPTION_WRONG_BG | OPTION_WRONG_BORDER |
页面根据是否提交、当前答案和正确答案选择颜色:
? Colors.OPTION_SELECTED_BG
if (key === question.answer) return Colors.OPTION_CORRECT_BG
这比在 Builder 中散落六七个十六进制值更容易审查,也能保证所有答案按钮状态一致。
六、颜色之外还要有结构令牌
Sizes 集中管理字号、圆角、图标、间距和固定控件高度:
TITLE_FONT = 16
SMALL_FONT = 10
PADDING_SMALL = 8
PADDING_XL = 20
librarya 版本还定义 TOUCH_TARGET = 44,并包含导航栏、搜索栏和 Banner 高度。它们共同决定页面节奏,而不仅是“几个常量”。
七、字号体系需要结合审核下限
当前 SMALL_FONT 为 10,CAPTION_FONT 为 12,BODY_FONT 为 14。10vp 小字主要用于 Tab 标签、徽标或次要信息,不应承担关键正文。
发布前应逐项检查:
- 正文优先保持 14vp;
- 说明与辅助文字保持 12vp;
- 10vp 只用于非常次要且短的标签;
- PC/2in1 常规文字应验证是否需要至少 14vp;
- 系统字体放大后容器不截断;
- 长文本有 maxLines、ellipsis 或可增长高度。
视觉令牌统一了默认值,但不能替代具体页面的可读性验证。
八、触控尺寸是视觉系统的一部分
44vp 的 TOUCH_TARGET 不只是交互常量,也决定图标按钮周围的留白。SearchPage 的返回、搜索等按钮使用固定触控容器,图标尺寸小于容器,既保持视觉轻量,又让点击区域足够大。
如果页面直接把 18vp 图标作为点击目标,即使颜色和圆角一致,交互体验仍不合格。令牌应区分:
- 图标视觉尺寸;
- 按钮触控尺寸;
- 控件间最小间距;
- 文本与图标的基线关系。
九、输入框令牌补足可编辑状态
librarya 的 Colors 还包含:
INPUT_BG
FavoritePage 和 PracticePage 的输入区域使用这些值,并把光标设为 PRIMARY、边框设为 DIVIDER。
输入状态至少需要验证默认、聚焦、已输入、占位、错误和禁用。当前项目集中管理了基础文本与背景,但没有完整的 focus/error 输入令牌矩阵;后续可补充 INPUT_FOCUS_BORDER、INPUT_ERROR_BORDER 和 DISABLED 系列。
十、组件消费语义令牌而不是页面猜颜色
共享组件 BankCard、GreenButton、ProgressBar、TopBar 和 EmptyState 都从 Colors/Sizes 取得样式。页面使用组件时只传业务数据和交互,不需要重新决定主色、卡片背景和圆角。
理想依赖方向是:
-> Feature Pages
页面不应反向定义组件颜色,也不应让不同业务模块复制同一套按钮样式。
十一、alpha 工具统一透明色格式
ThemeConstants 提供 alpha(color, alphaHex),把 #RRGGBB 转为 ArkUI 使用的 #AARRGGBB:
它可以用于投影、遮罩和浅色选中层,减少手工拼错 alpha 位置。
当前函数只做最小长度检查,没有验证输入一定是合法六位颜色,也不支持已有 alpha 的八位颜色。作为内部受控工具足够,若开放给更多模块,建议增加格式校验和明确返回类型。
十二、base 与 dark 资源当前完全相同
项目存在:
但两份内容相同,包括白色背景、绿色 primary 和灰色文字。dark 目录没有深色表面与浅色文字。
更关键的是,ArkTS 页面搜索不到 $r('app.color...') 的消费。页面实际使用的是 Colors 字符串常量。也就是说,这两套资源目前不是页面主题真值。
十三、资源颜色与实际蓝色主题已经漂移
资源文件的 primary 是 #4CAF50 绿色,而实际 Colors.PRIMARY 是 #2F6BFF 蓝色。start window 使用资源背景,页面进入后使用常量背景。
这会产生两个风险:
- 启动窗口与首个页面色彩不一致;
- 开发者误以为修改 color.json 就能改变全局主题。
主题系统必须只有一个清晰真源。若选择资源化,应把蓝橙方案迁入 base/dark 并让组件消费资源;若暂时保留常量,就应明确资源仅服务启动窗口,避免同名令牌表达不同色值。
十四、entry 与 librarya 存在重复 ThemeConstants
entry 目录和 librarya 都有 ThemeConstants。大多数页面从 librarya 导入 Colors/Sizes,entry 版本还包含题型图标和带印章资源的等级映射。
两份颜色常量当前大体一致,但已经出现能力差异:
- librarya 增加 INPUT_*;
- librarya 增加 TOUCH_TARGET;
- entry 的 RankInfo 包含 stamp Resource;
- entry 的题型函数引用 app.media。
重复文件会让“修改了一个却没有生效”变得常见。更合理的拆分是:
- librarya 保留纯颜色、尺寸和无 app 资源依赖的模型;
- entry 保留题型图标、印章等产品资源映射;
- entry 不再复制 Colors/Sizes。
十五、硬编码颜色并非全部错误
源码中 15 个 ArkTS 文件仍含十六进制颜色。它们大致分为三类。
合理局部色
- 图片上的半透明黑色遮罩;
- 白色渐变文字;
- 题型专属色;
- 特定插画背景。
应抽取的语义色
- 输入状态色;
- 收藏未选中颜色;
- 提示按钮颜色;
- 多处重复的弹窗遮罩。
应资源化的主题色
-主文字;
- 页面背景;
- 卡片表面;
- 分割线;
- 主品牌色。
目标不是消灭所有 #,而是消灭重复且具有跨组件语义的硬编码。
十六、遮罩透明度也应形成令牌
多个页面使用 #66000000 作为弹窗遮罩,卡片图片上还出现 #8A000000、#33000000 和 #29000000。
这些透明黑色有不同职责:
- ModalScrim:阻断背景交互;
- ImageScrimStrong:保证图片上白字可读;
- ImageScrimSoft:轻度压暗图片;
- ShadowColor:表达层级而非遮挡。
为它们命名后,设计调整不需要全仓搜索十六进制值,也能避免同一种遮罩在不同页面透明度漂移。
十七、状态不能只靠颜色表达
答题正确使用绿色、错误使用红色,但无障碍与低色觉用户不能只依赖色相判断。当前 PracticePage 同时会显示解析、边框与状态变化,结果页也有“正确/错误/未答”图例。
后续仍应检查:
- 正确项是否有文字或图标;
- 错误项是否显示实际选择;
- 选中项是否有边框或形状变化;
- 禁用项是否仍可辨认;
- 图例是否与实际网格一致;
- 高对比模式下语义是否保留。
十八、对比度必须用实际组合验证
只检查单个色值没有意义,对比度属于前景与背景组合。
重点组合包括:
| 前景 | 背景 | 场景 |
|---|---|---|
| TEXT_PRIMARY | SURFACE | 卡片正文 |
| TEXT_HINT | BACKGROUND | 辅助说明 |
| PRIMARY | PRIMARY_LIGHT | 标签与链接 |
| SUCCESS | OPTION_CORRECT_BG | 正确答案 |
| ERROR | OPTION_WRONG_BG | 错误答案 |
| 白色 | PRIMARY/PRIMARY_DARK | 渐变主卡 |
| 占位文字 | INPUT_BG | 输入框 |
正文目标应高于 4.5:1,关键图标和较大文字至少应高于 3:1。禁用态也不能淡到不可识别。
十九、浅色锁定下的系统栏必须一起设计
EntryAbility 设置了浅色模式,但当前文章范围内没有看到统一的窗口系统栏颜色策略。页面背景是近白蓝,系统栏图标应使用适合浅背景的深色样式。
需要实机验证:
- Splash 到 Index 时状态栏是否闪烁;
- 顶部背景变化时图标是否可读;
- 全屏图片页是否需要单独调整;
- 底部导航与系统导航区背景是否连续;
- 横屏和分屏时系统栏是否出现割裂。
锁定应用颜色模式并不会自动完成所有系统栏适配。
二十、如果未来跟随系统,应先资源化
真正的双主题不适合继续维护两套 ArkTS 字符串常量。更稳妥的路径是:
- 在 base color.json 定义浅色语义值;
- 在 dark color.json 用相同 name 定义暗色值;
- 组件改用
$r('app.color.token_name'); - 移除 EntryAbility 的浅色锁定或改为跟随系统;
- 为图片和图标提供可着色资源或暗色变体;
- 验证窗口生命周期中的模式变化。
同名资源是关键:组件只引用语义名称,系统根据 qualifier 选择实际色值。
二十一、暗色不是把背景改黑
暗色主题需要重新设计层级:
- 页面背景与卡片表面必须有可见差异;
- 主文字、次文字和提示文字重新建立亮度层级;
- PRIMARY_LIGHT 不能继续使用很亮的浅蓝底;
- SUCCESS/ERROR 在深色背景上需要调整明度与饱和度;
- 阴影可能需要改为描边或表面层级;
- 图片遮罩与白字组合要重新验证;
- 输入框、弹窗、底部栏和分割线全部需要暗色值。
直接沿用当前 dark 文件会得到白色背景,因此它不能算暗色实现。
二十二、主题迁移要分阶段避免大面积回归
建议按风险从低到高迁移:
第一阶段:清理真源
删除 Colors/Sizes 重复定义,统一由 librarya 提供;entry 只保留资源映射。
第二阶段:补齐语义
抽取遮罩、输入焦点、禁用、悬停、按压和收藏状态令牌。
第三阶段:资源化基础组件
先迁移 TopBar、GreenButton、BankCard、ProgressBar、EmptyState。
第四阶段:迁移页面
按 Home、Practice、Favorite、Settings 等主流程逐页替换。
第五阶段:建立 dark 值
完成双主题资源、移除浅色锁定,并运行全页面回归。
二十三、主题测试矩阵
| 维度 | 测试场景 |
|---|---|
| 模式 | 系统浅色、系统暗色、应用锁定浅色 |
| 页面 | 启动、首页、训练、挑战、收藏、我的、设置 |
| 状态 | 默认、选中、按压、禁用、正确、错误、警告 |
| 输入 | 空、占位、聚焦、已输入、错误 |
| 覆盖层 | 弹窗、底部 Sheet、图片遮罩 |
| 设备 | 手机、平板、2in1、小窗、横屏 |
| 字体 | 默认、放大、长文本 |
| 系统区 | 状态栏、导航栏、手势区 |
每个场景都要检查颜色、对比度、文字溢出、图标可见性和操作状态。
二十四、视觉令牌也需要自动检查
可在本地增加轻量审计脚本:
- 扫描页面中的硬编码颜色;
- 检查重复色值是否已有令牌;
- 对关键前景/背景组合计算对比度;
- 检查 base/dark 是否拥有相同资源名;
- 检查 dark 值是否与 base 完全相同;
- 检查字号是否低于项目下限;
- 检查触控容器是否小于 44vp。
脚本不能替代目视检查,但能提前拦截明显漂移。
二十五、发布审核前的视觉检查
发布前应确认:
- App 图标在浅色、暗色、启动页和商店中清晰;
- 应用实际是锁定浅色还是跟随系统,文档与代码一致;
- 状态栏、导航栏和页面背景连续;
- 正文对比度高于 4.5:1;
- 关键图标和大文字对比度高于 3:1;
- 正确、错误、警告不只靠颜色;
- 弹窗遮罩下内容仍可辨认;
- 输入框占位和禁用态不过淡;
- 字体放大后按钮和卡片不截断;
- 所有设备形态没有白字白底或深字深底。
二十六、常见问题排查表
| 症状 | 可能原因 | 检查点 |
|---|---|---|
| 修改 color.json 页面不变 | 页面使用 Colors 字符串 | 导入来源 |
| 系统暗色下仍是浅色 | EntryAbility 强制 LIGHT | 当前产品策略 |
| dark 目录存在但页面不暗 | dark 值与 base 相同 | 资源内容 |
| 启动页绿色、页面蓝色 | 资源与常量主题漂移 | start window |
| 某些输入框颜色不同 | librarya 与 entry 常量能力不同 | 重复 ThemeConstants |
| 弹窗遮罩深浅不一 | 透明黑色硬编码 | 抽取 Scrim 令牌 |
| 正确答案不易识别 | 只依赖绿色 | 增加文字、图标和边框 |
| 小字难读 | SMALL_FONT 用于关键内容 | 字号职责 |
| 深色迁移后浅蓝底刺眼 | PRIMARY_LIGHT 未提供暗色值 | 语义资源 |
| 2in1 控件难操作 | 只验证颜色,未验证输入 | 焦点与触控尺寸 |
二十七、总结:令牌统一的是语义,不只是色值
口算王当前视觉系统的真实基础是:应用锁定浅色,librarya 的 Colors/Sizes 提供蓝橙品牌、表面层级、三级文字、状态色、答题选项色、字号、间距、圆角和触控尺寸,共享组件与页面广泛消费这些令牌。这已经明显降低了页面割裂。
同样需要正视三项未完成工作:base/dark 资源仍是相同的旧绿色方案,页面没有消费 app.color 资源,entry 与 librarya 存在重复主题常量,部分语义颜色仍散落在页面。
下一步不应直接“加一套黑色”,而应先统一主题真源、补全语义令牌、迁移共享组件,再建立同名的浅色和暗色资源。视觉令牌真正统一的是“这个颜色在表达什么”,只有语义稳定,亮暗色、多设备和交互状态才能一起演进。
本文部分内容由 AI 辅助整理,所有现有行为、颜色值、资源结构与问题边界均依据上述本地源码复核;资源化双主题、令牌清理、自动对比度审计与迁移阶段部分为基于当前实现的工程建议。


二十三、从锁定浅色迁移到双主题的最小实施顺序
如果团队决定从当前的锁定浅色迁移到跟随系统,第一步不应立即删除 COLOR_MODE_LIGHT,而是先消除令牌源的分叉。entry 与 librarya 同时维护 ThemeConstants,意味着相同名字可能在两个模块中得到不同值;页面一旦混用,后续很难判断某个颜色究竟来自哪个版本。应先确定唯一公共出口,逐个修正导入路径,再用搜索确认旧出口没有剩余调用。
第二步是处理硬编码色值。搜索十六进制颜色只是入口,还要区分品牌常量、临时调试值、透明遮罩和真正的页面私有颜色。能够表达业务含义的值应改成语义令牌,例如“主按钮背景”“次级文字”“错误边框”,而不是按视觉外观命名为“深蓝”“浅灰”。这样即使暗色模式下实际色值变化,组件含义仍然稳定。
第三步才是资源化。为 base 与 dark 提供同名颜色资源,并让令牌层通过资源引用获得实际值。资源名称应表达角色而不是模式,例如 page_background、surface_primary、text_secondary,不要创建 light_background 和 dark_background 后再由页面手工判断模式。模式解析应交给资源系统,业务页面只消费统一语义。
第四步是补齐交互状态。按钮、输入框、列表项、答案选项和开关不能只验证默认状态。至少应覆盖默认、按下、选中、聚焦、禁用、正确、错误和加载中,并检查每个状态在浅色与暗色下是否仍可区分。状态不能只依赖颜色,还应结合图标、边框、文字或形状反馈,避免色觉差异用户无法理解。
第五步才移除强制浅色并进行验证。验证范围应包含首次启动、前后台切换、系统模式实时切换、状态栏与导航栏、弹窗、键盘、长文本、字体放大、小窗、横竖屏以及平板或 2in1。图片和自定义图标也要单独检查,因为它们可能不会自动响应资源变化。
这套顺序的关键是把“代码收敛”“资源映射”和“运行验证”拆开。若三件事同时进行,一旦出现文字不可见或页面闪色,很难定位是重复常量、错误资源还是生命周期刷新导致。逐层迁移虽然步骤更多,但每一步都有可搜索、可回滚、可复核的验收点。
二十四、发布前可执行的视觉令牌检查表
提交前可以用一份短检查表降低遗漏概率:确认页面不新增散落色值;确认所有关键文字和背景达到可读对比;确认 10vp 小字不承载核心信息;确认图标视觉尺寸与触控区域分离;确认正确、错误、警告没有混用语义;确认禁用状态仍能看清但不会误导为可点击;确认状态栏和导航栏在实际背景上可读;确认系统字体放大后按钮和卡片不会截断。
对于当前口算王工程,这份检查表只能作为下一轮验证计划。本文没有执行构建、模拟器或真机切换,因此不能据此宣称暗色适配、对比度审核或多设备体验已经通过。可靠的结论仍然是:工程已经建立较完整的浅色令牌基础,同时存在双份常量、未差异化 dark 资源和硬编码色值等迁移风险。
AI 辅助声明
本文在人工复核口算王 ArkTS 源码、主题常量和资源文件后,使用 AI 辅助整理结构、润色表达并生成配图;未执行的构建、真机、暗色切换和审核均未写成已通过。
更多推荐

所有评论(0)