Files
seclusion/apps/web/src/lib/theme-generator.ts
charilezhou 5c1a998192 feat(web): 实现自定义主题功能
支持 6 套预设主题(默认、海洋、森林、日落、玫瑰、紫罗兰)和自定义主色色相滑块,
通过动态注入 CSS 变量实现主题切换,使用 localStorage 持久化存储,
添加 SSR 初始化脚本避免首次加载颜色闪烁。

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 18:22:22 +08:00

140 lines
3.6 KiB
TypeScript

/**
* 主题色板生成器
* 根据主色色相自动生成完整的浅色/深色主题色板
*/
import type { HSLColor, ThemePalette } from '@/config/theme-presets';
// 创建 HSL 颜色
function hsl(h: number, s: number, l: number): HSLColor {
return { h, s, l };
}
// 确保色相在 0-360 范围内
function normalizeHue(hue: number): number {
return ((hue % 360) + 360) % 360;
}
/**
* 生成浅色主题色板
* @param primaryHue 主色色相 (0-360)
*/
export function generateLightPalette(primaryHue: number): ThemePalette {
const hue = normalizeHue(primaryHue);
return {
// 背景色系 - 纯白到微灰
background: hsl(0, 0, 100),
foreground: hsl(hue, 84, 4.9),
// 卡片
card: hsl(0, 0, 100),
cardForeground: hsl(hue, 84, 4.9),
// 弹出层
popover: hsl(0, 0, 100),
popoverForeground: hsl(hue, 84, 4.9),
// 主色 - 深色调
primary: hsl(hue, 47.4, 11.2),
primaryForeground: hsl(hue - 12, 40, 98),
// 次要色 - 浅灰
secondary: hsl(hue - 12, 40, 96.1),
secondaryForeground: hsl(hue, 47.4, 11.2),
// 静音色
muted: hsl(hue - 12, 40, 96.1),
mutedForeground: hsl(hue - 7, 16.3, 46.9),
// 强调色
accent: hsl(hue - 12, 40, 96.1),
accentForeground: hsl(hue, 47.4, 11.2),
// 危险色 - 红色系
destructive: hsl(0, 84.2, 60.2),
destructiveForeground: hsl(hue - 12, 40, 98),
// 边框和输入框
border: hsl(hue - 8, 31.8, 91.4),
input: hsl(hue - 8, 31.8, 91.4),
// 聚焦环
ring: hsl(hue, 84, 4.9),
// 侧边栏 - 微带主色调的浅灰
sidebar: hsl(0, 0, 98),
sidebarForeground: hsl(hue + 18, 5.3, 26.1),
sidebarPrimary: hsl(hue + 18, 5.9, 10),
sidebarPrimaryForeground: hsl(0, 0, 98),
sidebarAccent: hsl(hue + 18, 4.8, 95.9),
sidebarAccentForeground: hsl(hue + 18, 5.9, 10),
sidebarBorder: hsl(hue - 2, 13, 91),
sidebarRing: hsl(hue - 5, 91.2, 59.8),
};
}
/**
* 生成深色主题色板
* @param primaryHue 主色色相 (0-360)
*/
export function generateDarkPalette(primaryHue: number): ThemePalette {
const hue = normalizeHue(primaryHue);
return {
// 背景色系 - 深色
background: hsl(hue, 84, 4.9),
foreground: hsl(hue - 12, 40, 98),
// 卡片
card: hsl(hue, 84, 4.9),
cardForeground: hsl(hue - 12, 40, 98),
// 弹出层
popover: hsl(hue, 84, 4.9),
popoverForeground: hsl(hue - 12, 40, 98),
// 主色 - 浅色调(深色模式下主色要亮)
primary: hsl(hue - 12, 40, 98),
primaryForeground: hsl(hue, 47.4, 11.2),
// 次要色 - 深灰
secondary: hsl(hue - 5, 32.6, 17.5),
secondaryForeground: hsl(hue - 12, 40, 98),
// 静音色
muted: hsl(hue - 5, 32.6, 17.5),
mutedForeground: hsl(hue - 7, 20.2, 65.1),
// 强调色
accent: hsl(hue - 5, 32.6, 17.5),
accentForeground: hsl(hue - 12, 40, 98),
// 危险色 - 深色模式下更暗
destructive: hsl(0, 62.8, 30.6),
destructiveForeground: hsl(hue - 12, 40, 98),
// 边框和输入框
border: hsl(hue - 5, 32.6, 17.5),
input: hsl(hue - 5, 32.6, 17.5),
// 聚焦环
ring: hsl(hue - 10, 26.8, 83.9),
// 侧边栏
sidebar: hsl(hue + 18, 5.9, 10),
sidebarForeground: hsl(hue + 18, 4.8, 95.9),
sidebarPrimary: hsl(hue + 2, 76.3, 48),
sidebarPrimaryForeground: hsl(0, 0, 100),
sidebarAccent: hsl(hue + 18, 3.7, 15.9),
sidebarAccentForeground: hsl(hue + 18, 4.8, 95.9),
sidebarBorder: hsl(hue + 18, 3.7, 15.9),
sidebarRing: hsl(hue - 5, 91.2, 59.8),
};
}
// HSL 颜色转换为 CSS 值字符串
export function hslToString(color: HSLColor): string {
return `hsl(${color.h} ${color.s}% ${color.l}%)`;
}