支持 6 套预设主题(默认、海洋、森林、日落、玫瑰、紫罗兰)和自定义主色色相滑块, 通过动态注入 CSS 变量实现主题切换,使用 localStorage 持久化存储, 添加 SSR 初始化脚本避免首次加载颜色闪烁。 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
140 lines
3.6 KiB
TypeScript
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}%)`;
|
|
}
|