HelloWorld 亮色模式教程
要在 HelloWorld 应用里做好“亮色模式”,关键在于把颜色、版式和交互状态抽象成一套可切换的设计代号(即 design tokens),在不同平台用各自的机制去切换与持久化,注意对比度与图标/图片的适配,以及页面首屏主题的同步以避免闪烁。下面我会一步步拆解原理、具体实现(Web、iOS、Android、Flutter、React Native)和测试要点,让你能把一个简单的 HelloWorld 变成体验良好的亮色/暗色双主题应用。

先说为什么要认真做亮色模式
很多人只关注暗色模式,事实上亮色模式并不是“默认”就安全。不同设备、不同光照下亮色的可读性、对比和眩光问题都会影响用户体验。把亮色模式做成和暗色一样严谨,能让品牌在白天环境、办公场景或印刷输出时都显得更专业。
几点直观的好处
- 可读性稳定:亮色在强光下表现更好,长时间阅读时对眼睛的刺激不同。
- 品牌一致性:品牌色在亮背景上通常更接近原始视觉识别。
- 无障碍合规:按 WCAG 检测可以确保高对比度场景下仍可被访问。
实现原理:把颜色抽象成 Token
费曼式的做法就是把复杂问题拆成简单块。首先,把颜色、字号、间距、圆角这些做成一套 token(变量)。然后在运行时应用不同 token 的值来切换主题。这样你就只需维护一套逻辑,而不是每个组件里改颜色。
为什么用 Token 而不是直接改色值
- 维护成本低:统一修改一个 token,即可影响全局。
- 跨平台复用:同一套设计系统可以映射到 CSS 变量、iOS 资产、Android 主题和 Flutter 的 ThemeData。
- 便于可访问性检查:token 层能统一检查对比度。
| Token 名 | 亮色值 | 用途 |
| bg-primary | #FFFFFF | 主背景 |
| bg-surface | #F7F7F8 | 卡片/表单背景 |
| text-primary | #0B0B0C | 主文本 |
| text-secondary | #6B6B70 | 辅助文本 |
| brand | #0A84FF | CTA / 品牌色 |
Web 实战:从 CSS 变量到无闪烁切换
做法大致三步:定义变量、响应系统偏好、实现手动切换并持久化。别忘了在首屏用内联脚本优先设置主题,避免 FOUC(闪烁)。
1. 基本变量结构(示例)
在 CSS 中用根变量管理颜色。例如:
:root { --bg-primary: #FFFFFF; --text-primary: #0B0B0C; --brand: #0A84FF; }
2. 响应 prefers-color-scheme(系统偏好)
@media (prefers-color-scheme: dark) { :root { --bg-primary: #0B0B0C; --text-primary: #FFFFFF; } } 这段让浏览器自动在系统为暗色时应用暗色 token。
3. 手动切换与持久化
用一个开关控制 html 元素的 data-theme 属性或类名,并把选择保存到 localStorage。关键是要在 CSS 加载前执行检测并设置好类,避免白屏/闪烁。
可在页面头部插入一小段内联脚本(加载 CSS 前)来读取 localStorage 并设置 document.documentElement.setAttribute('data-theme', 'light') 或 ‘dark’。
4. 注意事项与调试
- 确保图片和 SVG 能根据主题切换(使用 CSS filter、SVG currentColor 或多色资产)。
- 检查表单控件、滚动条、选中样式等原生元素在亮色下的可见性。
- 使用浏览器的“强制颜色方案”和 Lighthouse 检查对比度。
iOS(SwiftUI / UIKit)实现要点
iOS 平台自带深色/亮色的资源管理。关键是把颜色资产分类为 Any / Dark。对于亮色模式,你要提供合适的 Color Set 默认值(Light),并在需要时覆盖。
SwiftUI(推荐)
- 使用 Color(“brand”) 读取资源目录里的颜色。
- 在 Assets.xcassets 为每个颜色添加 Light / Dark 的变体,或只填 Light 并让系统根据需求处理。
- 如果需要强制亮色,可以在 SceneDelegate(UIKit 生命周期)或 SwiftUI 的 windowScene 中设置
window.overrideUserInterfaceStyle = .light(注意审核与用户体验)。
UIKit 小技巧
在 Storyboard 或代码中使用系统颜色(例如 labelColor)可以减少维护。但如果用了自定义颜色,务必在 Color Assets 提供两套值。
Android(Material / AppCompat)实作要点
Android 推荐使用 DayNight 主题和资源限定符。把亮色主题放在 values/ 和 colors.xml 中,暗色放在 values-night/。
步骤要点
- 在 styles.xml 定义 Theme.MaterialComponents.Light.Bridge 或继承自己的亮色主题。
- 实现切换时用 AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO / MODE_NIGHT_YES / MODE_NIGHT_FOLLOW_SYSTEM)。
- 为图片准备两套资源(drawable/ 和 drawable-night/),或使用矢量图(svg/vector)并着色。
Flutter:一处定义,多平台复用
Flutter 的 ThemeData 支持 light 和 dark。把颜色 token 放入 ThemeData.light().copyWith(…) 和 ThemeData.dark(),通过 Provider/Bloc 或 系统开关让应用切换。
关键代码思路
在 MaterialApp 中传入 theme 与 darkTheme,然后通过 themeMode 切换(ThemeMode.light / dark / system)。把颜色 token 放在一个独立类里,便于复用。
React Native:用 Appearance 与 Context
React Native 提供 Appearance.getColorScheme(),结合 useColorScheme 或监听颜色变化可以实时响应系统偏好。持久化用 AsyncStorage。
- 对图标使用 react-native-svg 并通过 fill={theme.colors.text} 控制颜色。
- 不要忘了 StatusBar 的样式(light-content 或 dark-content)。
图片与图标的适配策略
图片通常是最让人头疼的部分。简单策略有三种:
- 双色/透明图标:用单色 SVG/矢量图,通过 CSS/代码着色(currentColor)来适配。
- 两套位图:为亮色/暗色分别准备图片,按主题加载。
- 动态滤镜:对位图使用滤镜(如 CSS filter)做简单反转,但质量有限。
无障碍与可读性:对比度与可访问性检查
遵循 WCAG 的对比度建议:普通文本至少 4.5:1,较大文本至少 3:1。用工具(Lighthouse、axe、Contrast Checker)在不同主题下分别检测。
常见问题与解决办法
- 问题:浅灰文本在强光下看不清。解决:提高 text-primary 对比度或增大字号/字重。
- 问题:背景渐变在亮色下造成可读性下降。解决:为文字区域加遮罩或使用低饱和度渐变。
- 问题:切换时闪烁或样式不同步。解决:首屏用内联脚本优先设置主题并在用户切换时写入 localStorage/Preferences。
测试清单(Checklist)
- 在强光和弱光环境下手动检查主要页面。
- 对比度检测通过(WCAG 自动化工具 + 人工复核)。
- 图标、图片在亮色下正确显示(无残影或不协调的装饰)。
- 表单控件、弹窗、Tooltip 在两套主题下行为一致。
- 主题切换的持久化(首次加载、用户切换、系统改变)行为正确。
性能与发布注意
把主题资源做成轻量的 token(变量),避免在运行时大量重绘。Web 首屏的内联脚本要短小以降低阻塞;移动端要确保主题变更不会触发全量重绘或导致卡顿。
版本控制与设计协作
把 token 存在一个单独的 repo 或设计系统里(JSON、YAML),并写转换脚本输出到 CSS 变量、iOS 颜色资源、Android colors.xml、Flutter 的 Dart 文件中。这样设计改动能自动同步到工程。
一个简单的 HelloWorld 实例思路(Web)
做个轻量的示例:index.html 头部内联脚本读取 localStorage,如果有用户偏好就设置 data-theme。主样式用 :root 和 [data-theme=”dark”] 两组变量。按钮触发切换并持久化。这样既支持系统偏好,又支持用户选择。
示例流程(步骤)
- 定义 token(colors.json)。
- 生成 CSS 变量文件并引入。
- 头部内联脚本设置 data-theme,避免闪烁。
- 实现开关组件并保存到 localStorage。
- 测试对比度、图片、状态控件。
常见误区(别踩的坑)
- 误以为亮色就是白底黑字:亮色也要讲层次感(卡片、分隔线、阴影)。
- 只在 CSS 层面处理,忽略原生控件(比如 iOS 的 UIBarButtonItem)在亮色时的样式。
- 图片没有做适配导致在亮背景出现“黑边”或不和谐的背景色。
结语(随手记)
把亮色模式当成一次设计完善的机会,它不仅仅是调亮背景色,更是把信息层次、交互状态和品牌色在光照良好的环境下打磨到位。做 token、统一流程、在每个平台用本地化机制去实现切换,这三步能大幅降低后期维护成本。顺带提醒一句:别忘了把设计改动记录在变更日志里,那样团队合作会顺一点(好吧,有时候我就是忘了写日志,然后追着改半天)。