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

发布时间:2026/9/8 19:26:31
【句匠|17】HarmonyOS ArkTS 亮暗色与视觉令牌实战:集中颜色、间距和交互状态避免页面割裂 一个 HarmonyOS 应用页面增加到十几个之后视觉问题往往不是“某个颜色不好看”而是同一种语义在不同页面被写成不同值标题有时 18vp、有时 20vp卡片圆角一会儿 12vp、一会儿 18vp成功状态和主按钮都用绿色却没有区分品牌色与语义色暗色模式下某些页面自动变暗另一些页面仍固定白底。这样的应用单页看似正常连续使用时却会明显割裂。本文基于句匠项目D:\huawei\one18-11的真实 ArkTS 源码重点核对entry/src/main/ets/common/constants/ThemeConstants.ets、EntryAbility.ets、resources/base/element/color.json、resources/dark/element/color.json并抽查TopBar.ets、GreenButton.ets、BankCard.ets、SectionHeader.ets、PracticePage.ets、SettingsPage.ets等页面和组件。本文唯一源码标识com.jiaweikang.one18。先说结论句匠已经把大量颜色、字号、间距、圆角和状态色集中到Colors与Sizes共享组件也在复用这些令牌但应用当前明确锁定浅色模式dark 资源与 base 资源值相同ArkTS 颜色常量又是固定十六进制字符串。因此源码可以证明的是“浅色视觉体系已经部分令牌化”不能证明“亮暗色自动适配已经完成”。一、视觉令牌解决的不是美术问题而是语义一致性视觉令牌是把“为什么使用这个值”变成稳定名称。比如Colors.TEXT_PRIMARY Colors.TEXT_HINT Colors.SUCCESS Sizes.BODY_FONT Sizes.CARD_RADIUS Sizes.PADDING_LARGE页面不需要记住标题到底是#1F2A26只需要表达“这是主要文本”。以后调整配色时修改令牌即可影响所有正确引用它的页面。视觉令牌通常分成三层层级示例作用原始值#2F6B5E、16vp最底层数值语义令牌PRIMARY、TEXT_HINT描述用途组件令牌按钮背景、卡片圆角描述具体组件状态句匠已有前两层的一部分也有答题选项这一类接近组件级的状态令牌。二、句匠的品牌色如何被集中ThemeConstants.ets将英伦学院风定义为墨绿、米白与暗金export class Colors { static readonly PRIMARY: string #2F6B5E static readonly PRIMARY_DARK: string #1F5246 static readonly PRIMARY_LIGHT: string #E6EFEB static readonly ACCENT: string #8A6814 static readonly INK: string #3A4A45 }这些值承担不同职责PRIMARY用于主按钮、进度、选中状态PRIMARY_DARK用于按压感或渐变下端PRIMARY_LIGHT用于标签底色和选中区域浅底ACCENT用于金色点缀INK作为墨色扩展。把“深一点的绿色”命名为PRIMARY_DARK比页面里散落#1F5246更容易维护。设计调整时也能知道它与主色的关系。三、背景与表面必须分开项目定义了三种背景层级static readonly BACKGROUND #F5F1E8 static readonly BACKGROUND_ALT #FBF8F0 static readonly SURFACE #FFFFFFBACKGROUND是页面底SURFACE是卡片与工具栏BACKGROUND_ALT是卡片内部的次级区域。即使都接近浅色语义仍不同。如果所有区域都直接写Color.White页面层级只能依赖阴影和边框如果每个页面随意挑一种米白又会出现轻微但持续的色差。令牌让页面背景、卡片表面与嵌套区域保持稳定关系。四、文本颜色要按信息层级定义真实源码中有static readonly TEXT_PRIMARY #1F2A26 static readonly TEXT_SECONDARY #444444 static readonly TEXT_HINT #5F6763它们分别服务标题、正文、辅助信息。TEXT_HINT的注释明确写了“符合 4.5:1”说明项目在调整辅助文字时考虑了正文可读性而不是只追求浅灰效果。页面使用时应按语义选择Text(题库) .fontColor(Colors.TEXT_PRIMARY) Text(按地区与题型挑选练习内容) .fontColor(Colors.TEXT_HINT)同一页面内如果标题、正文、占位符、禁用文本都用同一种灰色信息层级会消失如果辅助文字过浅又会成为 AppGallery 色彩对比风险。五、状态色不能由品牌色代替句匠单独定义static readonly SUCCESS #267A55 static readonly ERROR #B3261E static readonly WARNING #8A5A00虽然品牌主色也是绿色但成功色仍有独立语义。这样未来品牌色改成蓝色时“答题正确”不需要跟着变成蓝色。错误、警告、成功还应同时使用文字、图标或形状表达不能只依赖色相。对于色觉差异用户仅靠红绿判断选项正误并不可靠。当前项目已有正确/错误背景与边框令牌后续可以结合图标或明确文案继续增强。六、答题选项已经形成组件状态矩阵Colors对答题选项定义了完整状态OPTION_BG OPTION_BORDER OPTION_SELECTED_BG OPTION_SELECTED_BORDER OPTION_CORRECT_BG OPTION_CORRECT_BORDER OPTION_WRONG_BG OPTION_WRONG_BORDER这比页面里写一串三元表达式更稳。一个答题选项至少包含默认、选中、正确、错误四种状态每种状态又有背景与边框两个维度。状态矩阵可以写成状态背景边框文本/图标默认OPTION_BGOPTION_BORDER主要文本选中OPTION_SELECTED_BGOPTION_SELECTED_BORDER主色强调正确OPTION_CORRECT_BGOPTION_CORRECT_BORDER成功语义错误OPTION_WRONG_BGOPTION_WRONG_BORDER错误语义这类组件级令牌最能防止 PracticePage 多种题型之间出现交互状态不一致。七、字号、间距和圆角同样是令牌Sizes并不只有颜色static readonly H1_FONT 22 static readonly H2_FONT 18 static readonly TITLE_FONT 16 static readonly BODY_FONT 14 static readonly CAPTION_FONT 12 static readonly SMALL_FONT 10同时还集中CARD_RADIUS CARD_RADIUS_SM BTN_RADIUS PADDING_SMALL PADDING_MEDIUM PADDING_LARGE PADDING_XL TAB_BAR_HEIGHT BOTTOM_NAV_MIN_PADDING这让视觉一致性从颜色扩展到排版、空间与形状。TopBar、SectionHeader、BankCard和GreenButton都在引用这些值。需要注意SMALL_FONT 10对手机辅助信息尚可但不能无差别用于正文在 PC/2in1 场景10vp 也可能偏小。令牌化不代表数值永远正确它只是让后续统一调整变得可能。八、共享组件是令牌真正落地的位置仅定义Colors和Sizes不会自动获得一致性页面必须通过共享组件消费它们。TopBar.ets使用.colorBlend(Colors.PRIMARY) .backgroundColor(Colors.SURFACE) .border({ width: 1, color: Colors.DIVIDER }) .fontSize(Sizes.H2_FONT) .fontColor(Colors.TEXT_PRIMARY)GreenButton.ets使用主色与深主色构建渐变.linearGradient({ angle: 135, colors: [ [Colors.PRIMARY, 0], [Colors.PRIMARY_DARK, 1] ] })BankCard.ets则统一卡片背景、标题、标签、辅助文字和圆角。共享组件覆盖得越多页面越不容易自行发明另一套样式。九、alpha 工具如何减少透明色散落项目提供export function alpha( color: string, alphaHex: string ): string { if (!color || color.length 7) return color return # alphaHex color.substring(1, 7) }ArkUI 十六进制颜色采用#AARRGGBB这个工具把基础颜色和透明度组合。比如alpha(Colors.SUCCESS, 15)比直接写#15267A55更能看出语义关系。真实边界也要写清楚该函数只取color.substring(1, 7)适用于项目当前的#RRGGBB字符串如果传入资源颜色、短格式、已有 Alpha 或非十六进制表示就不一定符合预期。它不是通用颜色解析器。十、当前应用明确锁定浅色模式EntryAbility.onCreate()中存在this.context .getApplicationContext() .setColorMode( ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT )这意味着应用启动时主动选择浅色而不是跟随系统暗色模式。调用放在try/catch中失败会记录日志但正常情况下用户切换系统暗色不会让应用进入真正暗色主题。因此标题里的“亮暗色”应理解为对现状与迁移方法的实战分析不能解释为项目当前已提供亮暗色开关。十一、dark 资源存在但内容与 base 完全相同项目同时存在resources/base/element/color.json resources/dark/element/color.json但两个文件中的start_window_background、primary、background、surface、text_primary等值完全相同例如背景都为#F5F5F5表面都为#FFFFFF。这说明资源目录结构已经准备好但暗色值并没有设计完成。即使移除强制浅色如果页面引用这些资源暗色限定目录也不会产生视觉变化。更关键的是主要页面实际大量引用ThemeConstants.ets中的固定字符串而不是$r(app.color.background)。资源限定机制无法自动替换这些 ArkTS 常量。十二、当前存在两套并不一致的颜色来源ThemeConstants.ets的主色是#2F6B5E资源color.json的primary却是#4CAF50前者背景是米白#F5F1E8后者是灰白#F5F5F5。这构成双重真源ArkTS Colors.PRIMARY → #2F6B5E resource app.color.primary → #4CAF50如果启动页、原生配置或少数页面使用资源而大部分页面使用 ArkTS 常量就可能在启动过渡、对话框或系统组件上看到配色跳变。真正稳定的主题体系需要指定唯一真源。对于需要系统亮暗色限定的颜色优先使用同名资源ArkTS 中可以保留尺寸、业务映射和辅助函数但不应再复制一套固定颜色值。十三、硬编码颜色仍然大量存在源码抽查显示多个页面除了Colors.*之外还直接使用Color.White #E6FFFFFF #33000000 #29000000 #00000000 #B3000000部分硬编码是合理的例如深色渐变上的白字与半透明遮罩但它们仍需进入主题审计。暗色模式下固定白字可能落在浅色背景上固定黑色遮罩可能使内容过暗。迁移时可以将常见叠加层集中HERO_TEXT HERO_TEXT_SECONDARY SCRIM_LIGHT SCRIM_STRONG PRESSED_OVERLAY这段命名是建议不是当前源码已有字段。十四、真正双主题应怎样迁移建议按风险从低到高分四步先确定是否跟随系统、提供应用内切换还是继续锁定浅色统一颜色真源把页面级固定颜色迁移到语义资源为 dark 限定目录填写真实暗色值移除强制浅色并做全页面回归。资源可以保持同名{ color: [ { name: background, value: #F5F1E8 }, { name: surface, value: #FFFFFF }, { name: text_primary, value: #1F2A26 } ] }dark 目录使用同样名称但填写经过对比度验证的深色值。页面引用.backgroundColor($r(app.color.background)) .fontColor($r(app.color.text_primary))这样系统资源解析才会根据限定目录切换。十五、不能机械反转浅色调色板暗色主题不是把白变黑、黑变白。句匠的墨绿和暗金在深色背景上可能失去对比度浅绿色标签底也不能直接沿用。暗色设计至少要重新确定语义浅色关注点暗色关注点页面背景米白氛围避免纯黑造成强烈反差卡片表面与背景分层比背景略亮并保持边界主文本深色高对比近白但避免刺眼辅助文本满足 4.5:1不使用过暗灰主色品牌识别提升亮度或降低饱和分割线可见但克制避免完全消失成功、错误、警告状态还要分别检查文字、背景和边框组合不能只验证单个色值。十六、组件状态要覆盖按压、禁用与聚焦当前GreenButton能表达主按钮与部分视觉变化答题选项也有选中、正确、错误状态。但完整的 PC/2in1 和多输入体验还需要普通按压禁用键盘聚焦鼠标悬停加载中操作成功或失败。视觉令牌可以继续扩展BUTTON_PRIMARY_BG BUTTON_PRIMARY_PRESSED_BG BUTTON_DISABLED_BG BUTTON_DISABLED_TEXT FOCUS_RING HOVER_OVERLAY是否需要全部字段取决于组件不应一次性制造庞大体系。但关键工作流至少要有可识别的禁用与反馈状态。十七、主题切换必须连同系统栏和启动页验证主题不仅存在于 ArkUI 页面。句匠还涉及start_window_background状态栏图标明暗底部导航指示区对话框与弹窗图片、图标和渐变空状态与错误状态TextArea 占位符Canvas 或自绘内容。如果启动页仍是白色而首屏变为深色会出现明显闪白。若状态栏背景变深但图标仍使用深色模式图标会不可见。移除COLOR_MODE_LIGHT前必须把这些系统表面一起纳入验收。十八、用可执行矩阵检查对比度发布前不应只凭肉眼说“看起来清楚”。建议至少检查正文文字 / 页面背景 4.5:1 标题与关键图标 / 背景 3:1 按钮文字 / 按钮背景 3:1 辅助文字 / 卡片表面 4.5:1 禁用态仍能识别但不与可用态混淆矩阵需要覆盖浅色与暗色、普通与按压、选中与未选中、正确与错误、占位符、分割线和弹窗。句匠当前注释提到了TEXT_HINT对比度但源码中没有完整自动化对比度测试记录因此不能把整个应用描述为已经全部测量通过。十九、如何减少页面割裂从真实源码出发最有效的整理顺序是保留Colors、Sizes已形成的语义命名让TopBar、GreenButton、BankCard、SectionHeader等共享组件成为页面默认入口统计页面中的硬编码颜色与尺寸按出现频率迁移合并 ArkTS 常量与资源颜色双重真源再设计 dark 限定资源和系统栏策略最后移除强制浅色并做真机/模拟器回归。这样可以先获得一致性收益再承担主题切换风险。直接删除setColorMode()只会把未完成的 dark 资源和固定十六进制颜色同时暴露出来。二十、结语先把现状说清楚再谈暗色适配句匠真实源码已经完成了一项有价值的基础工作墨绿、米白、暗金、文本层级、状态色、答题选项状态、字号、间距、圆角和安全区尺寸大量集中在ThemeConstants.ets共享组件也在持续消费这些令牌。这能显著降低页面各写一套样式的风险。但“存在 dark 目录”不等于支持暗色“定义 Colors 类”也不等于具备动态主题。当前应用锁定浅色dark 与 base 资源相同主要 ArkTS 颜色是固定字符串资源文件与主题常量还存在不同配色。这些都是必须如实记录的工程边界。一个可靠的 HarmonyOS 5.0 及以上主题体系应以语义令牌为中心以同名亮暗资源为唯一颜色真源让共享组件统一消费并对系统栏、启动页、图片和完整交互状态做对比度回归。做到这一步亮暗色适配才不是“换一张色表”而是整个应用视觉语义的一致切换。本文部分内容由 AI 辅助整理所有能力判断均基于句匠真实源码未把浅色锁定状态描述成已完成的系统暗色适配也未虚构审核或发布结果。