主题系统
主题由 swift.config.json 的 theme.mode 单旋钮驱动,一个字段同时决定三件事:加载哪套 token、声明什么 color-scheme、是否提供切换器。
theme.mode 四选一
// nuxt.config.ts
swiftConfig: {
theme: {
mode: 'toggle', // 'system' | 'toggle' | 'dark' | 'light'
default: 'auto', // 仅 toggle:切换器初始态 'auto' | 'light' | 'dark',默认 'auto'
toggleStates: 3, // 仅 toggle:3=三态(可回自适应)/ 2=两态(仅明暗),默认 3
}
}
mode | 加载的 token | color-scheme | 切换器 | 典型场景 |
|---|---|---|---|---|
'system' | 浅 + 暗 | 暗色语境下 dark(跟随系统) | ❌ 不提供 | 想跟随系统、但不给用户手动切 |
'toggle' | 浅 + 暗 | 暗色语境下 dark | ✅ useTheme() + SwThemeSwitch | 用户可切明暗并记忆 |
'dark' | 固定深色 | dark | ❌ | 固定深色站(调色板由 applier overrides 提供) |
'light'(starter 默认) | 固定浅色 | light | ❌ | 固定浅色站,公司多数项目 |
theme 只接受对象形态。固定单主题站直接写 { mode: 'light' } 或 { mode: 'dark' }。
每种 mode 都会让原生 UI(滚动条、下拉、日期选择器等)跟对,避免「深色页面配浅色滚动条」这类割裂,但声明方式按 mode 分两类:固定
dark/light在:root上常驻声明;system/toggle只在暗色语境(@media (prefers-color-scheme: dark)与[data-theme='dark'])声明color-scheme: dark,浅色语境不声明、走浏览器默认(等效浅色)。
工作原理
toggle 模式下,框架在 <html> 上设置 data-theme="light|dark",CSS 用属性选择器覆盖变量。框架还会自动注入一段 head 内联 bootstrap script:首帧渲染前同步读取 localStorage 中的用户偏好并抢先设置 data-theme,避免「手选主题与系统偏好相反时整页加载闪一帧错色」(FOUC)——无需业务配置。变量覆盖示例:
:root {
--background: #ffffff;
--foreground: #1a1a1a;
}
[data-theme='dark'] {
--background: #0f0f12;
--foreground: #e5e7eb;
}
auto(跟随系统)用 prefers-color-scheme 媒体查询检测系统偏好,并在系统偏好变化时自动切换。固定 dark / light 模式则由框架注入对应的 color-scheme 声明、不挂 data-theme 切换逻辑。
useTheme() 使用
<script setup lang="ts">
const { theme, resolvedTheme, mode, toggleTheme, setTheme } = useTheme()
</script>
<template>
<p>当前主题:{{ theme }}(实际明暗:{{ resolvedTheme }})</p>
<!-- 仅 toggle 模式下 toggleTheme / setTheme 生效;其它模式为 no-op -->
<button @click="toggleTheme()">切换主题</button>
<button @click="setTheme('dark')">深色</button>
<button @click="setTheme('light')">浅色</button>
<button @click="setTheme('auto')">跟随系统</button>
</template>
theme是用户选择的状态('auto' | 'light' | 'dark'),resolvedTheme是当前实际明暗('light' | 'dark',auto态取系统偏好)——切 logo / 插画按resolvedTheme判断。toggleTheme()/setTheme()仅在mode: 'toggle'时生效,其它模式为 no-op(主题由 mode 锁定)。
详细 API 参见 useTheme() composable 文档。
内置切换器 SwThemeSwitch
不想自己写切换按钮时,直接放框架内置的 SwThemeSwitch——它由 useTheme() 驱动、自 gate:仅 mode: 'toggle' 时渲染按钮,其它模式渲染为空。改 theme.mode 配置,切换器自动出现 / 消失,无需改模板。
<template>
<!-- 默认内联 SVG 图标(sun / moon / auto 半圆);mode≠toggle 时自动不显示 -->
<SwThemeSwitch />
<SwThemeSwitch :size="22" />
<!-- 框架不绑图标库:用具名 slot 传入自己图标库的图标 -->
<SwThemeSwitch>
<template #light><PhSun :size="18" /></template>
<template #dark><PhMoon :size="18" /></template>
<template #auto><PhCircleHalf :size="18" /></template>
</SwThemeSwitch>
</template>
sizeprop:默认内联图标尺寸(px,默认 18;用 slot 传自定义图标时不生效)。- slot
#light/#dark/#auto:覆盖对应态图标,作用域提供{ theme, resolved }(#auto仅三态出现)。 - 皮肤走
--sw-theme-switch-*token(尺寸 / 圆角 / hover 背景等),applier 可覆盖。
持久化
用户选择存在 localStorage 的 theme key,刷新后自动恢复。为在首屏 inline script 阶段同步读取避免 FOUC,useTheme() 用裸 localStorage(不走加密缓存 util)——这是框架认可的例外,业务做主题切换时与它共用同一 key 即可。
自定义主题变量
/* 在 assets/styles/global.css 中覆盖框架默认变量 */
:root {
--primary: #6366f1;
}
[data-theme='dark'] {
--primary: #818cf8;
}
固定 dark 模式下,直接在 :root 定义深色调色板即可(无需 [data-theme] 选择器,因为不切换)。