HarmonyOS深色模式适配实战——主题切换与WCAG对比度标准
技术栈:HarmonyOS 5.0 + ArkTS + AppStorage
适用场景:应用主题切换、无障碍适配、华为应用市场审核
前言
深色模式已成为现代应用的标配功能。华为应用市场审核对深色模式有严格要求,所有文字必须清晰可见。本文将介绍如何实现符合WCAG标准的深色模式适配。
一、WCAG对比度标准
WCAG(Web Content Accessibility Guidelines)定义了文字对比度标准:
| 级别 | 正常文本 | 大文本 |
|---|---|---|
| AA级 | ≥ 4.5:1 | ≥ 3:1 |
| AAA级 | ≥ 7:1 | ≥ 4.5:1 |
二、主题颜色定义
2.1 浅色主题
export const LightTheme: ThemeColors = {pageBg: '#F8F9FA',cardBg: '#FFFFFF',cardBgSecondary: '#F5F5F5',// 文字色 - 符合WCAG标准textPrimary: '#1A1A1A', // 对比度 16.1:1textSecondary: '#595959', // 对比度 7.0:1textTertiary: '#737373', // 对比度 4.6:1divider: '#EEEEEE',border: '#E5E5E5',buttonBg: '#F0F0F0',shadowColor: 'rgba(0, 0, 0, 0.08)',
};
2.2 深色主题
export const DarkTheme: ThemeColors = {pageBg: '#121212',cardBg: '#1E1E1E',cardBgSecondary: '#2A2A2A',// 文字色 - 符合WCAG标准textPrimary: '#FFFFFF', // 对比度 15.3:1textSecondary: '#CCCCCC', // 对比度 9.7:1textTertiary: '#999999', // 对比度 5.1:1divider: '#333333',border: '#404040',buttonBg: '#2A2A2A',shadowColor: 'rgba(0, 0, 0, 0.3)',
};
2.3 主题接口定义
export interface ThemeColors {pageBg: string;cardBg: string;cardBgSecondary: string;textPrimary: string;textSecondary: string;textTertiary: string;divider: string;border: string;buttonBg: string;shadowColor: string;
}
三、主题管理器
export class ThemeManager {private static isDarkMode: boolean = false;private static listeners: Array<(isDark: boolean) => void> = [];static setDarkMode(isDark: boolean): void {ThemeManager.isDarkMode = isDark;ThemeManager.listeners.forEach(listener => listener(isDark));}static getIsDarkMode(): boolean {return ThemeManager.isDarkMode;}static getTheme(): ThemeColors {return ThemeManager.isDarkMode ? DarkTheme : LightTheme;}static addListener(listener: (isDark: boolean) => void): void {ThemeManager.listeners.push(listener);}static removeListener(listener: (isDark: boolean) => void): void {const index = ThemeManager.listeners.indexOf(listener);if (index > -1) ThemeManager.listeners.splice(index, 1);}
}
四、页面中使用主题
4.1 使用AppStorage实现全局状态
@Entry
@Component
struct MainPage {@StorageLink('appDarkMode') isDarkMode: boolean = true;getBgColor(): string { return this.isDarkMode ? '#0D1117' : '#F5F5F5'; }getTextPrimary(): string { return this.isDarkMode ? '#FFFFFF' : '#1A1A1A'; }getTextSecondary(): string { return this.isDarkMode ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.6)'; }private toggleTheme(): void {this.isDarkMode = !this.isDarkMode;AppStorage.setOrCreate('appDarkMode', this.isDarkMode);PreferencesUtil.putBoolean('app_dark_mode', this.isDarkMode);}build() {Column() {Text('标题').fontSize(24).fontColor(this.getTextPrimary())Text('副标题').fontSize(14).fontColor(this.getTextSecondary())Button('切换主题').onClick(() => this.toggleTheme())}.backgroundColor(this.getBgColor())}
}
4.2 跟随系统主题
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';@Entry
@Component
struct SettingsPage {@StorageProp('currentColorMode') @Watch('onColorModeChange') currentColorMode: number = ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT;@State themeMode: string = 'system'; // 'system' | 'light' | 'dark'onColorModeChange(): void {if (this.themeMode === 'system') {const isDark = this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;AppStorage.setOrCreate('appDarkMode', isDark);}}private applyThemeMode(): void {let isDark: boolean;if (this.themeMode === 'system') {isDark = this.currentColorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK;} else {isDark = this.themeMode === 'dark';}AppStorage.setOrCreate('appDarkMode', isDark);PreferencesUtil.putString('theme_mode', this.themeMode);}
}
五、对比度检查工具
推荐使用在线工具检查颜色对比度:
- WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
- Coolors Contrast Checker: https://coolors.co/contrast-checker
六、避坑指南
- 华为审核要求:深色模式下所有文字必须清晰可见
- @StorageLink vs @StorageProp:前者双向绑定,后者只读
- 颜色透明度:使用
rgba()时注意透明度对对比度的影响 - 图标适配:深色模式下图标也需要适配
总结
本文介绍了HarmonyOS深色模式的完整实现方案,包括符合WCAG标准的颜色定义、主题管理器、页面使用方法等。正确的深色模式适配不仅能通过应用市场审核,还能提升用户体验。