主题

前言

Simple 模板项目提供了一套灵活的主题与暗黑模式适配机制。它巧妙地结合了原生 CSS 变量(CSS Custom Properties)、Tailwind CSS 实用工具类、Element Plus 以及动态运行时换肤机制,使得开发者可以轻易地定制整站色调。

主题系统简介

模板项目的主题系统由以下三个核心层面共同构建:

  1. CSS 变量系统:定义在 src/styles/var.csssrc/styles/dark.css 中。通过在根节点(:root)与深色类选择器(html.dark)下重写同名 CSS 变量,实现全局样式的“明/暗切换”。
  2. Tailwind CSS (v4) 联动:在 src/styles/tailwind.css@theme 部分中,通过原生变量 var(--u-...) 将自定义 CSS 变量注册到 Tailwind 的类名系统中。这样,开发者在业务代码中即可通过如 bg-bodytext-tx-primary 的实用程序类,自动获得符合明暗模式的颜色表现。
  3. 运行时主题色生成:通过前端辅助函数根据用户指定的主题色(如 Primary 品牌色),在运行时计算出 Element Plus 规范所需的明暗梯度色,并动态插入 <style id="theme-vars"> 覆盖默认表现。

主题样式变量结构

项目的核心基础变量全部以 --u- 前缀命名,从而与浏览器默认样式和框架底层的 Element Plus 变量区分。

样式变量分类列表

以下是模板中最为常用的关键 CSS 变量及其含义:

变量分类CSS 变量名称默认明色值 (var.css)默认暗色值 (dark.css)样式含义与用途
页面与容器背景--u-bg-color-page#fff#0a0a0a页面最底层背景色
--u-bg-color#fff#1d2124基础卡片与容器背景色
--u-bg-color-overlay#fff#1d1e1f弹出层、对话框、下拉菜单背景色
文本字色--u-text-color-primary#1d2130#e5eaf3主要文字、标题色
--u-text-color-regular#666#cfd3dc常规正文、描述字色
--u-text-color-secondary#4e5968#a3a6ad次要文字字色
--u-text-color-placeholder#a8abb2#8d9095占位文字、禁用状态字色
边框与填充--u-border-color#dcdfe6#4c4d4f基础边框色
--u-border-color-light#e4e7ed#414243浅边框色
--u-fill-color#f0f2f5#303030区域填充色
--u-fill-color-light#f8f8f8#262727浅层填充色(如表格悬停、条纹)
阴影级别--u-box-shadow浅灰色阴影深黑色阴影基础卡片阴影

明暗模式切换原理

模板项目整合了 Vueuse、Pinia 以及系统偏好偏好,提供了极其顺畅的明暗转换。

颜色模式与核心变量

系统配置项中支持三种颜色模式:

  • light:强制使用明色主题;
  • dark:强制使用暗色主题;
  • system:跟随操作系统当前的明暗状态偏好。

该参数定义在 src/config/setting.ts 中,并在 Pinia 状态树的 settingStore.colorMode 中进行维护。

全局类名与样式切换流

明暗主题的自动切换和解析流程如下:

  1. 获取原始偏好:系统初始化或用户手动切换模式时,读取全局 colorMode 配置以及系统级的 prefersDark 状态偏好。

  2. 状态逻辑解析:调用 resolveIsDark(mode, systemPrefersDark) 辅助函数计算得出最终是否应当启用暗色的布尔值(isDark)。

  3. 切换全局样式类

    • isDarktrue(启用暗色):通过 DOM 操作为 html 根节点添加 class="dark"
    • isDarkfalse(启用明色):将 class="dark"html 根节点中移除。
  4. 生成运行时变量:同步调用 setTheme 函数重新计算主色彩梯度并在 document.head 中动态插入或更新相应的 CSS 变量样式,确保明暗状态下 Element Plus 和系统主色的完美呈现。

  5. 在顶层组件 src/App.vue 中,使用 Vueuse 的 usePreferredDark 响应式监听系统的暗黑偏好:

    ts
    const prefersDark = usePreferredDark()
    watch(
      [prefersDark, () => settingStore.colorMode],
      () => {
        settingStore.applyThemeForResolvedDark(prefersDark.value)
      },
      { immediate: true }
    )
  6. applyThemeForResolvedDark 接收系统偏好,结合用户在 store 中的 colorMode 设置决定是否启用暗色,最终通过 document.documentElement.classList.toggle('dark', isDark) 切换 html 上面的 dark 类。

  7. html 带有 .dark 类名时,:root.dark 中的 CSS 变量值(来自 dark.css)将覆盖 :root 下的明色变量值,整站外观自适应转换。


运行时主题色动态生成

除了简单的明暗模式,模板项目还支持在运行时动态地对主色调(Primary 品牌色)及其他辅助状态色(Success, Warning, Danger 等)进行“品牌换肤”。

主题梯度色计算逻辑

动态生成变量的核心文件在 src/utils/theme.ts

在运行时更改品牌色时,系统会加载 css-color-function 插件,根据基色和混色配置,通过 shadetint 变换函数计算出一组梯度配色。 例如,以 lightConfig 为例,生成的变量规则为:

  • dark-2shade(20%)
  • light-3tint(30%)
  • light-5tint(50%)
  • light-7tint(70%)
  • light-8tint(80%)
  • light-9tint(90%)

而在暗黑模式下,配置会切换为 darkConfig,以获取反向更深/更亮梯度色,确保深色模式下按钮及文字对比度清晰。

动态样式表插入机制

当品牌色变量被 generateVars 计算完毕后,将调用 setTheme 将这些变量渲染为 CSS 的 :root 代码段,并追加到 <head> 中:

ts
export const setTheme = (options: Record<string, string>, isDark = false) => {
  // 根据当前色彩选项与明暗状态,得到完整的 CSS 变量 Mapping 键值对
  const varsMap = Object.keys(options).reduce((prev, key) => {
    return Object.assign(prev, generateVars(options[key], key, isDark))
  }, {})

  // 格式化为 CSS 样式内容
  let theme = Object.keys(varsMap).reduce((prev, key) => {
    const color = colors.convert(varsMap[key])
    return `${prev}${key}:${color};`
  }, '')
  theme = `:root{${theme}}`

  // 创建或更新动态样式标签插入到文档头部
  let style = document.getElementById('theme-vars')
  if (style) {
    style.innerHTML = theme
    return
  }
  style = document.createElement('style')
  style.setAttribute('type', 'text/css')
  style.setAttribute('id', 'theme-vars')
  style.innerHTML = theme
  document.head.append(style)
}

这套动态生成的 --u-color-primary 变量会被 Element Plus 以及 Tailwind CSS 底层捕获,达到一键换肤的效果。


如何自定义主题

修改默认颜色配置

若需要在初始状态修改默认的品牌色彩或明暗模式配置,请直接编辑项目根配置 src/config/setting.ts 中的 defaultSetting 项:

ts
const defaultSetting = {
  // ...
  theme: '#2F54EB', // 修改默认系统主题品牌色
  successTheme: '#42B200', // 修改默认成功主题色
  colorMode: 'system', // 修改默认色彩模式 (light / dark / system)
}

覆盖特定 CSS 变量

  1. 若需修改全局的明色样式或特定组件基础色,可在 src/styles/var.css 中修改:
    css
    :root {
      /* 覆盖主要字色为极深蓝色 */
      --u-text-color-primary: #0f172a;
    }
  2. 若需自定义或修补组件库在深色模式下的效果(例如编辑器 WangEditor ),可修改 src/styles/dark.css注意:在 src/styles/index.scss 中需取消对 dark.css 的导入注释):
    css
    :root.dark {
      /* 覆盖编辑器区域在暗黑下的背景 */
      --w-e-textarea-bg-color: var(--u-bg-color);
      --w-e-textarea-color: var(--u-text-color-primary);
    }

联动 Tailwind CSS

如果您在写自定义组件时想利用配置系统中的颜色变量,我们强烈推荐使用 Tailwind 实用程序类。

因为在 src/styles/tailwind.css 中已经配置了变量桥接映射:

css
@theme {
  /* 桥接自定义的 CSS 变量到 Tailwind 类名系统中 */
  --color-body: var(--u-bg-color);
  --color-page: var(--u-bg-color-page);
  --color-tx-primary: var(--u-text-color-primary);
  --color-tx-regular: var(--u-text-color-regular);
}

您在业务 Vue 页面中,直接使用:

vue
<template>
  <!-- 当系统处于明色时背景为白色,字色为深灰;暗色时背景变为 #1d2124,字色变为 #e5eaf3 -->
  <div class="bg-body text-tx-primary p-4 border border-br">
    <h3>卡片标题</h3>
    <p class="text-tx-regular">常规描述正文内容...</p>
  </div>
</template>

这种设计最大程度做到了“样式逻辑与业务表达分离”,降低了多主题、多明暗状态下的心智负担。