Skip to content

深色模式

Sakana Element 内置了完整的深色模式支持,采用 Catppuccin Mocha 风格的配色方案。主题系统提供三种模式 —— lightdarksystem —— 并自动将选择保存到 localStorage,同时支持检测系统偏好。

名称分类说明类型
theme? 暴露当前主题设置ComputedRef<'light' | 'dark' | 'system'>
isDark? 暴露当前是否为深色模式ComputedRef<boolean>
prefersDark? 暴露操作系统是否偏好深色模式Ref<boolean>
prefers? 暴露操作系统配色方案偏好Ref<'light' | 'dark'>
setTheme? 方法设置主题为 'light'、'dark' 或 'system'(theme: Theme) => void
toggleTheme? 方法在 light 和 dark 之间切换() => void

基本用法

使用 useTheme 组合式函数来切换主题。主题状态在所有组件之间共享,并持久化到 localStorage。

<template>
  <div class="demo-dark-mode">
    <div class="demo-dark-mode__status">
      <span>Theme: <code>{{ theme }}</code></span>
      <span>isDark: <code>{{ isDark }}</code></span>
    </div>
    <div class="demo-dark-mode__actions">
      <px-button @click="toggleTheme">Toggle Theme</px-button>
      <px-button type="primary" @click="setTheme('light')">Light</px-button>
      <px-button type="primary" @click="setTheme('dark')">Dark</px-button>
      <px-button type="info" @click="setTheme('system')">System</px-button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useTheme } from 'sakana-element';
import { useData } from 'vitepress';
import { watch } from 'vue';

const { isDark, theme, toggleTheme, setTheme } = useTheme();
const { isDark: vpIsDark } = useData();

// Sync VitePress → useTheme (immediate: true aligns initial state)
watch(
  vpIsDark,
  (dark) => {
    if (dark !== isDark.value) setTheme(dark ? 'dark' : 'light');
  },
  { immediate: true },
);

// Sync useTheme → VitePress
watch(isDark, (dark) => {
  if (dark !== vpIsDark.value) vpIsDark.value = dark;
});
</script>

<style scoped>
.demo-dark-mode__status {
  display: flex;
  gap: 20px;
  margin-bottom: 16px;
  font-size: 14px;
}

.demo-dark-mode__status code {
  padding: 2px 6px;
  border: 1px solid var(--px-border-color-lighter);
  background: var(--px-fill-color-lighter);
}

.demo-dark-mode__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
</style>

主题模式

setTheme 函数接受三个值:

模式说明
'light'强制浅色模式,不跟随系统设置
'dark'强制深色模式,不跟随系统设置
'system'自动跟随用户操作系统的配色方案
ts
import { useTheme } from 'sakana-element'

const { setTheme, toggleTheme, isDark, theme } = useTheme()

// 设置指定模式
setTheme('dark')

// 在浅色 ↔ 深色之间切换(不涉及 system)
toggleTheme()

// 读取当前状态
console.log(theme.value)  // 'light' | 'dark' | 'system'
console.log(isDark.value)  // true | false

持久化

调用 setTheme() 时,选择的模式会保存到 localStoragepx-theme 键下。页面刷新后主题会自动恢复。

系统偏好检测

useSystemTheme 组合式函数提供对用户操作系统配色方案的只读访问。当系统偏好发生变化时,它会响应式地更新。

vue
<script setup lang="ts">
import { useSystemTheme } from 'sakana-element'

const { prefersDark } = useSystemTheme()
</script>

<template>
  <p>系统偏好深色模式:{{ prefersDark }}</p>
</template>

什么时候用哪个?

使用 useTheme 来控制主题(切换、持久化、应用 CSS 类)。使用 useSystemTheme 仅需要读取系统偏好而不改变任何东西时 —— 例如,显示"您的系统处于深色模式"的提示。

工作原理

当深色模式激活时,Sakana Element 会在 <html> 元素上添加 px-dark 类。所有组件样式都引用 CSS 自定义属性(变量),这些变量在 .px-dark 下重新定义:

:root            →  浅色模式变量(默认)
.px-dark, .dark  →  深色模式变量覆盖

这意味着主题切换是纯 CSS 实现的 —— 不需要组件重新渲染。

自定义颜色

你可以覆盖任意一个 CSS 变量来定制主题。在你的应用全局 CSS 中定义覆盖即可:

<template>
  <div class="demo-custom-vars">
    <div class="demo-custom-vars__section">
      <h4>Default Theme</h4>
      <div class="demo-custom-vars__row">
        <px-button type="primary">Primary</px-button>
        <px-button type="success">Success</px-button>
        <px-button type="warning">Warning</px-button>
        <px-button type="danger">Danger</px-button>
      </div>
    </div>

    <div class="demo-custom-vars__section custom-theme">
      <h4>Custom Theme (overridden variables)</h4>
      <div class="demo-custom-vars__row">
        <px-button type="primary">Primary</px-button>
        <px-button type="success">Success</px-button>
        <px-button type="warning">Warning</px-button>
        <px-button type="danger">Danger</px-button>
      </div>
    </div>

    <div class="demo-custom-vars__code">
      <code>.custom-theme { --px-color-primary: #e64553; ... }</code>
    </div>
  </div>
</template>

<script setup lang="ts">
</script>

<style scoped>
.demo-custom-vars {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.demo-custom-vars__section {
  padding: 16px;
  border: 2px solid var(--px-border-color-lighter);
  background: var(--px-bg-color);
}

.demo-custom-vars__section h4 {
  margin: 0 0 12px;
  font-size: 14px;
  color: var(--px-text-color-primary);
}

.demo-custom-vars__row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.demo-custom-vars__code {
  padding: 10px 14px;
  font-size: 12px;
  background: var(--px-fill-color-lighter);
  border: 1px solid var(--px-border-color-lighter);
}

.demo-custom-vars__code code {
  color: var(--px-text-color-regular);
}

/* Custom theme overrides — this is what we're demonstrating */
.custom-theme {
  --px-color-primary: #e64553;
  --px-color-primary-dark: #c73a47;
  --px-color-primary-light-3: #eb6a76;
  --px-color-primary-light-5: #f08f98;
  --px-color-primary-light-7: #f5b4ba;
  --px-color-primary-light-8: #f8c7cc;
  --px-color-primary-light-9: #fce3e5;

  --px-color-success: #209fb5;
  --px-color-success-dark: #1a8599;

  --px-color-warning: #df8e1d;
  --px-color-warning-dark: #bf7a19;

  --px-color-danger: #d20f39;
  --px-color-danger-dark: #b30d31;
}
</style>

可用变量分类

分类示例变量说明
主色--px-color-primary--px-color-primary-dark品牌强调色
成功色--px-color-success--px-color-success-dark成功状态颜色
警告色--px-color-warning--px-color-warning-dark警告状态颜色
危险色--px-color-danger--px-color-danger-dark错误/危险状态颜色
信息色--px-color-info--px-color-info-dark信息状态颜色
背景色--px-bg-color--px-bg-color-page--px-bg-color-overlay页面和容器背景色
文字色--px-text-color-primary--px-text-color-regular排版颜色
边框色--px-border-color--px-border-color-light边框和分隔线颜色
填充色--px-fill-color--px-fill-color-light填充和背景强调色

TIP

每个语义颜色(primary、success 等)都有 -dark 深色变体和 -light-3-light-9 的浅色变体。覆盖这些变体可以在 hover、disabled 和 focus 状态下保持一致的外观。