# 暗黑模式

uview-plus 已提供完整的暗黑模式能力,包含:

  • 主题变量(亮/暗两套语义变量)
  • 运行时主题状态(系统跟随、手动切换、事件通知)
  • 页面与组件的统一接入方式(upTheme* 计算属性与工具方法)
  • Root 根容器联动(全局背景与变量下发)

本文档对应近期暗黑模式架构升级后的方案,不是旧版“下载主题 scss 文件”流程。

# 1. 架构概览

当前实现采用三层结构:

  1. 主题变量层
    文件:src/uni_modules/uview-plus/libs/css/theme-vars-core.scss
    职责:定义 --up-* 语义变量,并分别提供:

    • 默认亮色变量
    • prefers-color-scheme: dark 自动切换变量
    • [data-up-theme='light'|'dark'] 强制主题变量
  2. 运行时主题层
    文件:src/uni_modules/uview-plus/libs/theme/theme.js
    职责:统一管理主题状态与切换逻辑,支持:

    • system | light | dark 偏好模式
    • 本地存储主题偏好
    • 系统主题变化监听(uni.onThemeChange
    • 同步 uni.$u.themeuni.$u.colorconfig.color
    • 发送全局事件 uThemeChange
  3. 页面接入层
    文件:src/uni_modules/uview-plus/libs/mixin/mixin.jssrc/App.up.vue
    职责:为页面和组件提供统一可用的主题能力,减少页面重复代码。

# 1.1 uni-app 暗黑模式与 uview-plus 运行时主题的关系

uni-app 原生暗黑模式和 uview-plus 运行时主题不是二选一,而是分工不同:

  1. theme.json / pages.json 负责 uni-app 声明式默认主题,适合页面配置层面的默认值。

  2. uview-plus 运行时主题系统 负责应用运行过程中的主动同步,包括:

    • 系统跟随
    • 手动切换 light / dark / system
    • 本地记忆主题偏好
    • 同步导航栏、页面背景、tabBar、组件变量

因此,当运行时主题系统已经介入时,导航栏最终颜色不是单独从 theme.json 读取,而是以当前运行时主题结果为准。默认推荐通过 --up-navbar-bg-color 或对应运行时颜色配置统一管理导航栏背景色。

# 2. 可用 API

uview-plus 安装后可通过 uni.$u 使用以下接口:

// 强制切换当前主题(light / dark)
uni.$u.setTheme('dark')

// 设置主题偏好:system | light | dark
uni.$u.setThemePreference('system')

// 读取偏好与系统主题
uni.$u.getThemePreference()
uni.$u.getSystemTheme()

// 获取当前主题变量映射
uni.$u.getThemeVars()

主题切换后会触发全局事件:

uni.$on('uThemeChange', (payload) => {
  // payload.mode: light | dark
  // payload.vars: 当前变量字典
})

# 3. 页面推荐写法

页面默认可直接使用 mixin 提供的能力:

  • upThemeIsDark
  • upThemeVars
  • upThemePageStyle
  • upThemeCardStyle
  • upThemeVar(varName, fallback?)

示例:

<template>
  <view :style="upThemePageStyle">
    <view :style="upThemeCardStyle">内容区域</view>
  </view>
</template>

说明:

  • .vue / uvue 页面优先使用 CSS 变量(var(--up-xxx))能力。
  • nvue 不支持 CSS 变量时,使用 upThemeVar('--up-xxx', '#fallback') 获取运行时颜色。

# 4. Root 配合建议

若已启用 Root 注入(见 Root根组件),建议保留根容器统一背景与主题变量下发。
这样可以减少每个页面重复设置背景样式,避免出现“部分页面亮色、部分页面暗色”的断层。

# 5. 组件开发规范(暗黑适配)

组件样式建议遵循:

  1. 不直接写死颜色,优先使用语义变量
    例如:var(--up-content-color)var(--up-border-color)var(--up-bg-color)

  2. 组件内含状态色时,优先使用 --up-primary / --up-success / --up-warning / --up-error / --up-info 体系。

  3. 输入类组件必须关注三个对比度

    • 文本颜色
    • placeholder 颜色
    • 边框/背景颜色
  4. nvue 组件,统一通过运行时颜色映射做兜底。

# 6. 常见问题

# 6.1 页面仍是亮底

  • 检查是否使用了页面级硬编码背景色(覆盖了主题变量)。
  • 检查根容器是否正确挂载(App.up.vue + <UpRootView />)。
  • 检查主题切换是否实际触发(监听 uThemeChange 验证)。

# 6.2 组件在暗黑下“看不见”

常见原因:

  • 使用固定浅色文本(如 #333)但背景已变深。
  • placeholder 仍是浅灰,不符合暗底对比度。
  • 边框和背景使用同色,导致输入框轮廓消失。

建议优先替换为 --up-* 语义变量。

# 7. 迁移建议(从旧方案到新方案)

  1. 保留旧 theme.scss 兼容能力,但新页面优先使用运行时主题方案。
  2. 页面样式从“手工 if/else 颜色判断”迁移到 upThemePageStyle / upThemeVar
  3. 新增组件时直接按语义变量开发,避免后续二次暗黑改造成本。

# 8. 旧版 uni.scss 主题兼容边界

如果项目历史上在 uni.scss 中手写覆盖过 $u-primary$u-main-color$u-border-color 等变量,新版本会自动把这些旧值桥接到 light 主题--up-* 变量。

请注意:

  1. 这些旧 $u-* 只定义 light 主题,不会自动生成一套 dark 主题。
  2. 当主题模式为 dark 或系统处于暗色模式时,框架仍使用内置的 dark 语义变量。
  3. 如果业务需要品牌化 dark 主题,请直接按 --up-* / upThemeVar() 方案扩展,而不是继续只维护一份旧 $u-* 文件。
  4. 如果旧项目仍在 uni.scss 里手写 $u-*,请把这些变量写在 @import 'uview-plus/theme.scss'; 之前,确保 bridge 导出的是你的自定义值。
  5. 如果业务又调用了 setConfig({ color }) 或等价运行时改色逻辑,显式运行时颜色会优先于旧 $u-* bridge。