通过 Vike config、defineTheme() 和 useTheme() 管理主题,并在首屏前应用 CSS 变量。
主题系统
主题系统的目标很简单:把主题编译成稳定的 CSS 变量契约,并在首屏和运行时都应用到 document.documentElement。
当前数据流:
- 在
+config.ts里通过theme、themes、appearance提供默认主题。 vike-vue-content/config自动注册headHtmlBegin,在首屏前注入一段初始化脚本。- 初始化脚本优先读取
localStorage['vvc-theme'];如果没有用户覆盖,则回退到 Vike config 默认值。 useTheme()和ThemeSettings在客户端更新同一份状态,并把结果写回localStorage。- 主题 token 最终会展开成
--color-*、--font-*、--space-*和--radius。
这套设计是 client-authoritative:默认值来自 Vike config,用户修改后由客户端持久化覆盖。
快速开始
框架内置两个主题组件:
ThemeToggle:切换light/dark/systemThemeSettings:完整主题面板,支持 Primary、Neutral、Radius、Font 和导出
<script setup>
import { ThemeToggle } from 'vike-vue-content/components/theme-toggle'
import { ThemeSettings } from 'vike-vue-content/components/theme-settings'
</script>
<template>
<header>
<h1>My App</h1>
<div>
<ThemeSettings />
<ThemeToggle />
</div>
</header>
</template>记得在布局里引入:
import 'vike-vue-content/index.css'index.css 仍然需要,它提供组件样式和主题相关的基础样式。真正的 CSS 变量值不是静态写死在 CSS 里的,而是由首屏初始化脚本和 useTheme() 在运行时设置。
如果你使用 ThemeSettings,要注意它和 +config.ts 的关系:
+config.ts里的theme、themes、appearance决定站点默认主题ThemeSettings只是在客户端覆写这份默认值- 覆写结果存放在
localStorage['vvc-theme'] - 点击重置按钮后,会丢弃这层覆写,回退到
+config.ts默认配置
在 Vike 中配置默认主题
import type { Config } from 'vike/types'
import vikeVue from 'vike-vue/config'
import vikeVueContent from 'vike-vue-content/config'
import { defineTheme } from 'vike-vue-content/theme'
const brand = defineTheme({
name: 'brand',
fonts: {
sans: "'Open Sans', sans-serif"
},
radius: '0.5rem',
light: {
primary: '#8b5cf6',
neutral: 'slate'
},
dark: {
primary: '#8b5cf6',
neutral: 'slate'
}
})
export default {
extends: [vikeVue, vikeVueContent],
theme: 'brand',
themes: [brand],
appearance: 'system'
} satisfies Config几个字段的含义:
theme:当前启用的主题名themes:注册可用主题对象appearance:默认外观,取值为'light' | 'dark' | 'system'
如果你什么都不配,会回退到内置默认主题:blue + slate + Inter + 0.25rem。
defineTheme()
defineTheme() 接收的是 token 对象,返回规范化后的主题对象。
import { defineTheme } from 'vike-vue-content/theme'
const brand = defineTheme({
name: 'brand',
fonts: {
sans: "'Outfit', sans-serif"
},
radius: '0.375rem',
spacing: {
container: '1.5rem'
},
light: {
primary: '#0f766e',
neutral: 'slate',
bg: '#fcfffe'
},
dark: {
primary: '#0f766e',
neutral: 'slate',
bg: '#081311'
}
})规范如下:
light/dark下的字段会变成--color-*fonts下的字段会变成--font-*spacing下的字段会变成--space-*radius会变成--radius
其中有两个特殊 token:
primary- 支持命名色板,也支持 raw hex,例如
'#0066cc' - raw hex 会通过 Chroma.js 自动生成
primary-light和primary-dark
- 支持命名色板,也支持 raw hex,例如
neutral- 当前只支持命名中性色板
- 会自动展开出
muted、bg、surface、text、border等语义变量
如果浅色和深色共用一套颜色,也可以写 colors 作为两者的共同回退值。
内置预设
Primary 预设:
black red orange amber yellow lime green emerald teal cyan sky blue indigo violet purple fuchsia pink rose
Neutral 预设:
slate gray zinc neutral stone
Radius 预设:
0 0.125 0.25 0.375 0.5
Font 预设:
Inter system-ui Roboto Open Sans Montserrat Poppins Outfit Raleway
编程式控制
useTheme() 会读取当前默认主题,应用到 DOM,并把用户修改持久化到 localStorage。
<script setup lang="ts">
import { useTheme } from 'vike-vue-content/composables/theme'
const {
state,
theme,
isDark,
primary,
neutral,
radius,
font,
mode,
modes,
toggleDarkMode,
resetTheme,
exportCSS,
exportConfig,
exportVikeThemeConfig
} = useTheme()
</script>常用写法:
primary.value = 'violet'
primary.value = '#0066cc'
neutral.value = 'zinc'
radius.value = 0.5
font.value = 'Outfit'
mode.value = 'dark'说明:
mode对应的是appearancetoggleDarkMode()会按light -> dark -> system循环resetTheme()会清掉用户覆盖,回到 Vike config 默认值exportVikeThemeConfig()导出的是可直接放回themes的规范化 JSON
如果你只想操作持久化状态,不需要 DOM 应用逻辑,可以用 useThemeStorage()。
CSS 变量契约
主题系统对外暴露的是一组稳定变量:
| 变量 | 含义 |
|---|---|
--color-primary | 主色 |
--color-primary-light | 主色浅阶 |
--color-primary-dark | 主色深阶 |
--color-muted | 中性基准色 |
--color-muted-light | 中性浅阶 |
--color-muted-dark | 中性深阶 |
--color-bg | 页面背景 |
--color-surface | 表面背景 |
--color-surface-elevated | 浮层 / hover 背景 |
--color-text | 主文本 |
--color-text-muted | 次级文本 |
--color-text-dimmed | 弱化文本 |
--color-border | 边框 |
--color-border-muted | 柔和边框 |
--font-sans | 主字体 |
--radius | 圆角 |
--space-* | 自定义间距 token |
暗色模式激活时,根节点会带上 .dark 类,但通常你不需要手写 .dark 覆盖,直接消费变量就够了:
<style scoped>
.card {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: var(--radius);
}
.card:hover {
background: var(--color-surface-elevated);
}
.link {
color: var(--color-primary);
}
</style>导出与低层 API
ThemeSettings 面板里有两个导出按钮:
main.css:导出当前主题对应的 CSSvike-theme.json:导出可直接回填到themes的主题对象
如果你想在代码里自行处理,也可以直接使用 @vike-vue-content/theme 暴露的低层 API:
import {
exportThemeCss,
exportVikeThemeConfig,
themeToVars,
themeToCss,
themeToAppearanceCss
} from 'vike-vue-content/theme'适用场景:
- 生成静态 CSS 文件
- 把主题对象落盘到 JSON
- 自定义主题编辑器
- 在非 Vue 场景里复用同一套 token 编译逻辑