主題系統

主題由 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載入的 tokencolor-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>
  • size prop:預設內聯圖示尺寸(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] 選擇器,因為不切換)。