ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【中国方言题库|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂

2026/8/27 13:09:44 拓冰建站 浏览量
【中国方言题库|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂 主题适配常被简化成“准备一套浅色和一套深色颜色”但真正影响工程质量的是页面有没有统一使用语义颜色、字号、间距、圆角和交互状态。只要一部分页面直接写十六进制颜色另一部分引用常量一个模块把正文灰定义为#444444另一个模块定义为#3D4A45资源目录虽然有dark应用启动却强制锁定浅色那么“支持深色模式”就只是文件结构上的表象。中国方言题库的真实 ArkTS 源码提供了一个很典型的阶段性方案公共组件和业务页面大量引用Colors、Sizes形成墨绿、米白、白色卡片的统一视觉语言答题选项的默认、选中、正确、错误状态都有独立语义令牌输入框还显式定义文字、占位符和背景色避免系统颜色变化造成不可读。与此同时EntryAbility明确调用COLOR_MODE_LIGHT当前版本选择锁定浅色base与dark资源文件内容相同页面主令牌又是硬编码字符串所以不能宣称现有版本已经完成亮暗色自动切换。本文面向 HarmonyOS 5.0 及以上版本围绕真实源码解释视觉令牌怎样减少页面割裂、当前浅色锁定为什么能保持一致、若未来跟随系统深色模式需要改造哪些层。本文唯一核验标记视觉令牌统一语义颜色模式决定令牌如何解析。一、先核对真正生效的主题文件本文复核的主要文件是entry/src/main/ets/common/constants/ThemeConstants.ets librarya/src/main/ets/constants/ThemeConstants.ets entry/src/main/ets/entryability/EntryAbility.ets entry/src/main/resources/base/element/color.json entry/src/main/resources/dark/element/color.json entry/src/main/ets/common/components/GreenButton.ets entry/src/main/ets/common/components/BankCard.ets entry/src/main/ets/pages/PracticePage.ets entry/src/main/ets/pages/SettingsPage.ets entry/src/main/ets/views/HomePage.ets entry/src/main/ets/views/FavoritePage.ets队列 brief 指向了entry/src/main/ets/common/constants/ThemeConstants.ets该文件确实存在工程同时还有librarya的公共主题常量。多数页面通过import { Colors, Sizes } from librarya使用公共能力层令牌少量与资源图标、等级印章绑定的映射仍位于 entry。分析主题时不能只看一个同名文件否则会遗漏实际导入来源。二、当前版本究竟支不支持深色模式先给出结论当前应用正常启动后锁定浅色模式并没有跟随系统切换到深色模式。证据位于EntryAbility.onCreate()this.context .getApplicationContext() .setColorMode( ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT )调用被放在try/catch中失败时记录日志。只要设置成功应用就会使用浅色模式不会因为系统切到深色而自动改变。工程确实存在entry/src/main/resources/base/element/color.json entry/src/main/resources/dark/element/color.json但两个文件当前颜色值相同例如背景、表面和文本都还是浅色值。这说明dark目录更像占位或防御性资源并不是一套已完成的深色设计。更关键的是页面主要使用的Colors.PRIMARY、Colors.BACKGROUND等是 ArkTS 字符串常量static readonly BACKGROUND: string #F5F1E8 static readonly SURFACE: string #FFFFFF static readonly TEXT_PRIMARY: string #1F2A26它们不会因为资源限定目录变化而自动切换。即便移除浅色锁定仅修改dark/element/color.json这些硬编码令牌仍保持浅色。当前版本的正确描述是“视觉令牌集中管理并有明确的浅色锁定策略”而不是“已经支持亮暗色自适应”。三、为什么锁定浅色也必须认真设计锁定浅色并不等于可以忽略系统主题。用户可能在深色系统环境中启动应用启动窗口、状态栏、输入控件、弹窗和第三方组件都有可能受系统默认样式影响。如果页面背景是米白色而某个输入框文字沿用深色模式下的系统默认浅色就可能出现白底白字。librarya的Colors为输入组件增加了static readonly INPUT_TEXT: string #1F2A26 static readonly INPUT_PLACEHOLDER: string #6F7873 static readonly INPUT_BG: string #FFFFFF注释明确说明INPUT_TEXT用于显式覆盖系统深色默认色。这个处理与应用锁浅色的策略一致既然产品决定保持浅色就必须让容易受系统默认主题影响的组件也显式使用浅色令牌。锁定策略还应覆盖启动窗口。当前 base 和 dark 的start_window_background都是#FFFFFF避免系统深色时出现黑色启动窗口、进入应用后突然切到米白背景的闪变。它不是完整深色适配却是保持浅色视觉连续性的必要措施。四、Colors 的职责不是保存颜色名而是表达语义公共主题类没有按“绿色一、绿色二、灰色一”命名而是分为主色、背景、文本、状态和答题选项class Colors { static readonly PRIMARY #2F6B5E static readonly BACKGROUND #F5F1E8 static readonly SURFACE #FFFFFF static readonly TEXT_PRIMARY #1F2A26 static readonly TEXT_SECONDARY #3D4A45 static readonly TEXT_HINT #5F6763 static readonly SUCCESS #267A55 static readonly ERROR #B3261E }页面关心的是“这是页面背景”“这是主标题”“这是错误状态”而不是某个具体色值。未来品牌色调整时只需修改令牌定义使用Colors.PRIMARY的按钮、标签、进度和选中态会一起变化。语义化还能减少误用。例如“清除所有学习数据”使用Colors.ERROR正确答案使用Colors.SUCCESS普通辅助文案使用Colors.TEXT_HINT。如果所有页面只拿一个RED或GRAY开发者很难判断它该用于错误、危险操作、装饰还是禁用状态。五、背景层级如何避免页面像拼起来的中国方言题库使用三个主要背景层BACKGROUND - 页面底色米白 #F5F1E8 SURFACE - 卡片、按钮底、底部操作区白色 #FFFFFF BACKGROUND_ALT - 卡片内部说明区浅米白 #FBF8F0题库详情页根容器使用Colors.BACKGROUND学习摘要、简介、重点和章节卡片使用Colors.SURFACE简介卡片内部的文化提示使用Colors.BACKGROUND_ALT。三层关系稳定后页面之间不会出现一个用纯灰底、一个用米白底、一个又用未经约束的透明底。设置页也遵循相同模式页面根背景是BACKGROUND分组卡片是SURFACE选择器未选项是BACKGROUND_ALT。即使组件结构不同用户仍能识别“页面、卡片、内嵌选项”的视觉层级。六、Sizes 如何统一信息密度颜色只是视觉令牌的一部分。Sizes集中定义static readonly H1_FONT: number 22 static readonly H2_FONT: number 18 static readonly TITLE_FONT: number 16 static readonly BODY_FONT: number 14 static readonly CAPTION_FONT: number 12 static readonly SMALL_FONT: number 10 static readonly CARD_RADIUS: number 16 static readonly CARD_RADIUS_SM: number 10 static readonly BTN_RADIUS: number 24 static readonly PADDING_SMALL: number 8 static readonly PADDING_MEDIUM: number 12 static readonly PADDING_LARGE: number 16 static readonly PADDING_XL: number 20因此首页标题、页面二级标题、卡片标题、正文和辅助文字有清晰层级常规卡片圆角和小组件圆角也能保持一致。开发页面时不需要反复决定“这里到底是 15 还是 16”“这个卡片圆角是 14 还是 18”。但源码中仍有一些局部硬编码例如首页品牌标题使用 25考试页标题使用 24部分卡片使用圆角 18 或 22。这些值可能出于视觉强调并不自动构成错误真正的问题是它们没有语义名称后续统一调整时难以检索意图。建议把稳定复用的特殊规格补成PAGE_TITLE_FONT、HERO_RADIUS等令牌单次特例则保留在组件内部。七、交互状态为什么需要成组定义答题选项不是只有“选中”和“没选中”。公共令牌为它定义了完整状态OPTION_BG OPTION_BORDER OPTION_SELECTED_BG OPTION_SELECTED_BORDER OPTION_CORRECT_BG OPTION_CORRECT_BORDER OPTION_WRONG_BG OPTION_WRONG_BORDER这组设计把填充和边框成对组织。例如正确选项使用浅绿色背景配成功绿色边框错误选项使用浅红背景配错误红边框。页面不必临时拼接颜色也不容易出现“背景表达正确边框却仍是选中主色”的冲突。交互状态还需要文字和图标配合。只改变背景色对色觉差异用户不够友好题目页面还应通过正确/错误文案、图标或结果说明传达状态。当前令牌层解决的是颜色一致性不等于已经替代完整的无障碍反馈。八、GreenButton 如何复用主操作语言GreenButton支持实心和描边两种形态。实心按钮使用.fontColor(Color.White) .linearGradient({ angle: 180, colors: [ [Colors.PRIMARY, 0], [Colors.PRIMARY_DARK, 1] ] })描边按钮使用.fontColor(Colors.PRIMARY) .borderColor(Colors.PRIMARY) .backgroundColor(Colors.SURFACE)两个按钮共享Sizes.BODY_FONT圆角由高度的一半计算形成胶囊形操作按钮。题库详情页的“随机练习”和“模拟考试”、结果页的“错题解析”和“再考一次”都可使用同一组件主次关系保持一致。当前组件没有显式的 pressed、disabled、loading 参数。PRIMARY_DARK注释提到可用于按钮按压或渐变下端但现有GreenButton只把它用于渐变。若后续增加提交、保存或耗时操作应扩展状态接口而不是在各页面外层临时改透明度。九、选中态如何避免只靠一处变化设置页底部选择器的已选项同时改变多项属性.fontColor(selected ? Colors.PRIMARY : Colors.TEXT_PRIMARY) .fontWeight(selected ? FontWeight.Bold : FontWeight.Normal) .backgroundColor( selected ? Colors.PRIMARY_LIGHT : Colors.BACKGROUND_ALT )并额外显示“已选”标签Text(已选) .fontColor(Color.White) .backgroundColor(Colors.PRIMARY)这比只改变文字颜色更可靠。用户可以从字重、背景和标签三个维度识别状态。收藏页 Tab 也会在选中时使用主色背景和白色文字未选中时使用表面色与次要文字。不过点击、按压和键盘焦点状态在源码中没有形成统一令牌。对于手机触控选中态比较完整若面向 2in1 鼠标键盘还需要补充 hover、focus 和 pressed 的视觉反馈。十、alpha() 解决的是透明度复用不是主题切换两个主题文件都提供export function alpha(color: string, alphaHex: string): string { if (!color || color.length 7) return color return # alphaHex color.substring(1, 7) }ArkUI 使用#AARRGGBB该函数把透明度放在 RGB 前面。例如alpha(#2F6B5E, 15)得到#152F6B5E。它适合生成主色的低透明背景或阴影使同一品牌色在不同层级复用。函数假设传入的是至少七位的#RRGGBB字符串没有验证alphaHex是否为两位合法十六进制也不会正确处理资源对象或完整八位色。当前调用范围简单时足够如果主题系统扩展最好把输入约束写成更明确的接口避免把动态主题资源强行当字符串处理。十一、两个 ThemeConstants 为什么是潜在漂移点entry 与 librarya 都定义了Colors、Sizes和部分题型/等级映射。它们看起来接近但并不完全相同entry TEXT_SECONDARY #444444 librarya TEXT_SECONDARY #3D4A45 librarya 额外包含 INPUT_TEXT / INPUT_PLACEHOLDER / INPUT_BG librarya 额外包含 TOUCH_TARGET entry 的 RankInfo 包含 stamp Resource librarya 的 RankInfo 只有 label 和 color这说明两份文件已经承担不同职责librarya 是公共视觉令牌entry 还绑定了 app 资源。但重复定义同名Colors和Sizes会带来漂移风险。某个页面如果误从 entry 导入正文色就可能与从 librarya 导入的组件不同。更稳的边界是librarya - Colors、Sizes、纯数据映射 entry - 资源图标、印章 Resource、应用专属映射 - 直接复用 librarya 的 Colors这样颜色和尺寸只有一个真源entry 不再复制数值。十二、资源颜色与 ArkTS 字符串令牌目前没有打通color.json中定义了{ name: text_primary, value: #212121 }而Colors.TEXT_PRIMARY是static readonly TEXT_PRIMARY: string #1F2A26两者色值不同页面又主要使用后者。也就是说资源文件与 ArkTS 令牌是两套并行系统修改资源文件不会自动影响这些页面。这不是文章推测而是由引用方式直接决定的只有$r(app.color.text_primary)才会按资源限定目录解析普通字符串#1F2A26不会访问资源系统。如果产品继续锁浅色保留字符串令牌也能工作但应减少无效或过时的资源定义避免维护者误以为修改color.json已改变全局主题。如果计划支持跟随系统则应让语义令牌最终指向资源。十三、迁移到真正亮暗色适配的第一步未来若要跟随系统第一步不是删除COLOR_MODE_LIGHT而是先建立资源语义一致性。可在 base 中定义{ color: [ { name: app_background, value: #F5F1E8 }, { name: app_surface, value: #FFFFFF }, { name: text_primary, value: #1F2A26 }, { name: text_secondary, value: #3D4A45 }, { name: text_hint, value: #5F6763 } ] }dark 中使用同名资源{ color: [ { name: app_background, value: #111614 }, { name: app_surface, value: #1B211F }, { name: text_primary, value: #F2F5F3 }, { name: text_secondary, value: #CDD5D1 }, { name: text_hint, value: #AEB8B3 } ] }重点不是示例色值本身而是两套资源名称必须一致。页面只引用语义名系统根据当前颜色模式解析对应值。十四、第二步让组件消费资源而不是静态十六进制字符串页面可逐步从.fontColor(Colors.TEXT_PRIMARY) .backgroundColor(Colors.BACKGROUND)迁移为.fontColor($r(app.color.text_primary)) .backgroundColor($r(app.color.app_background))也可以建立一个能返回ResourceColor的主题令牌层统一封装$r引用。这里要注意类型设计现有Colors字段是string而资源引用属于Resource/ResourceColor使用场景。迁移不能只把字符串替换成$r而忽略接口类型。应优先迁移根背景、表面、主要文本、次要文本、分隔线和输入控件因为它们决定全页可读性品牌色和状态色可在确认两种模式对比度后再迁移。十五、第三步处理图片、阴影和覆盖层颜色资源切换后仍有几类视觉元素不会自动安全题库封面图片可能在深色背景中过亮。深色卡片上的CARD_SHADOW可能几乎不可见。#66000000蒙层在深色背景上层次不足。白色文字与渐变主色需要重新检查对比度。启动窗口、状态栏和导航栏图标颜色必须与背景一致。这些元素应在真机或模拟器中逐页验证不能只靠资源 JSON 静态检查。十六、对比度不是“看着清楚”就算通过当前令牌注释中TEXT_HINT标记为符合 4.5:1正文色也做了对比度增强。工程审查应按实际前景/背景组合计算而不是单独评价某个颜色。需要覆盖TEXT_PRIMARY / BACKGROUND TEXT_SECONDARY / SURFACE TEXT_HINT / BACKGROUND PRIMARY / PRIMARY_LIGHT White / PRIMARY ERROR / SURFACE SUCCESS / OPTION_CORRECT_BG INPUT_PLACEHOLDER / INPUT_BG 禁用态与半透明态正文通常应达到 4.5:1关键图标、按钮文字等至少应保持清晰可辨。深色模式不能简单把背景换黑、文字换白因为状态色在深色底上的亮度关系也会变化。十七、视觉令牌怎样减少跨页面割裂以首页、题库详情、收藏页和设置页为例它们的业务完全不同但共同使用BACKGROUND 作为根背景 SURFACE 作为卡片背景 TEXT_PRIMARY 作为标题 TEXT_HINT 作为辅助信息 PRIMARY 作为主要动作或选中状态 DIVIDER 作为列表分隔 CARD_RADIUS 作为常规卡片圆角 PADDING_LARGE 作为页面水平间距因此用户从首页进入详情再进入设置视觉层级仍是连续的。令牌系统的价值不只是“改色方便”而是把设计决定变成代码契约同一语义在不同页面必须有相同表现。反过来页面里仍存在Color.White、#E6FFFFFF、#66000000等直接值。白色用于主色渐变上的文字具有明确局部语义半透明蒙层也可能是组件特例并非所有硬编码都要消灭。应优先清理重复、跨页面使用且可能随主题变化的值。十八、主题状态不应和业务状态混在一起如果未来增加“跟随系统 / 浅色 / 深色”设置不建议把主题切换逻辑散落在页面。设置页只负责选择服务层负责保存偏好应用或主题控制器负责调用颜色模式 API。建议链路是设置页选择主题 - ThemePreferenceService 保存模式 - ThemeController 应用模式 - 资源系统重新解析同名颜色 - 页面自动刷新主题偏好是轻量设置可使用 Preferences页面不应在每次 build 时重复调用setColorMode。颜色模式 API 与 Ability 生命周期相关应由应用级边界统一管理。十九、当前版本适合执行的验证矩阵由于当前策略是锁定浅色测试重点不是“深色页面是否漂亮”而是“系统深色环境下浅色应用是否仍稳定”系统浅色 应用启动 系统深色 应用启动 运行中切换系统颜色模式 冷启动窗口到首页的背景连续性 输入框文字与占位符 状态栏和导航栏图标 设置页底部选择器 题目正确、错误、选中状态 清除数据危险操作 弹层蒙版和取消动作系统切换后应用应继续保持浅色不出现某些系统控件突然变暗、文字反色或启动页闪黑。若setColorMode调用失败日志会记录错误此时也需要观察回退表现。二十、未来跟随系统时的验收矩阵移除浅色锁定并完成资源迁移后再扩展为浅色所有主页面与二级页 深色所有主页面与二级页 运行中浅转深 运行中深转浅 冷启动恢复用户偏好 跟随系统模式 输入、弹窗、选择器、列表、卡片 正确、错误、警告、选中、禁用、按压 状态栏、导航栏、启动窗口 图片、图标、阴影、蒙层特别要检查设置页、练习页和考试结果页因为它们包含状态色、底部操作区和多层表面最容易出现局部遗漏。二十一、最小改造顺序建议按以下顺序推进避免一次性改完后难以定位问题1. 确认产品策略继续锁浅色还是跟随系统 2. 合并 entry 与 librarya 的重复 Colors / Sizes 3. 清理 base 与 dark 资源的同名同值占位 4. 将背景、表面、文本、分隔线迁移为资源令牌 5. 迁移输入框和弹层 6. 迁移状态色与答题选项 7. 增加主题设置与持久化 8. 验证状态栏、导航栏和启动窗口 9. 执行全页面对比度与截图回归如果产品决定继续锁浅色那么第 2、3 步仍然值得做统一令牌真源明确 dark 资源只是浅色锁定下的防御性镜像并在项目文档中记录策略。二十二、结语中国方言题库当前最真实的主题能力是一套覆盖颜色、字号、间距、圆角、输入和答题状态的视觉令牌以及明确的浅色锁定策略。它已经有效减少首页、题库、练习、收藏和设置之间的视觉割裂但还不是完整的亮暗色自动适配EntryAbility锁定浅色dark 资源与 base 相同主要页面又使用静态字符串色值。工程上最重要的判断是视觉令牌统一语义颜色模式决定令牌如何解析。先把同一语义收敛到唯一真源再决定锁浅色还是跟随系统如果选择深色适配就让令牌真正连接资源系统并验证输入、状态、系统栏和启动窗口。这样主题能力才不是一组散落色值而是一条可维护、可切换、可复核的视觉契约。AI 辅助声明本文在结构整理和语言润色环节使用了 AI 辅助关于浅色锁定、资源文件、颜色值、令牌重复和组件状态的描述均依据中国方言题库真实 ArkTS 与资源文件复核。