chore: Optimize multi-theme switching
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@vben-core/cache": "workspace:*",
|
||||
"@vben-core/colorful": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
|
@@ -18,7 +18,6 @@ const defaultPreferences: Preferences = {
|
||||
name: 'Vben Admin Pro',
|
||||
semiDarkMenu: true,
|
||||
showPreference: true,
|
||||
themeMode: 'dark',
|
||||
},
|
||||
breadcrumb: {
|
||||
enable: true,
|
||||
@@ -67,7 +66,13 @@ const defaultPreferences: Preferences = {
|
||||
showIcon: true,
|
||||
},
|
||||
theme: {
|
||||
colorPrimary: 'hsl(211 91% 39%)',
|
||||
builtinType: 'default',
|
||||
colorDestructive: 'hsl(348 100% 61%)',
|
||||
colorPrimary: 'hsl(245 82% 67%)',
|
||||
colorSuccess: 'hsl(144 57% 58%)',
|
||||
colorWarning: 'hsl(42 84% 61%)',
|
||||
mode: 'dark',
|
||||
radius: '0.5',
|
||||
},
|
||||
transition: {
|
||||
enable: true,
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import type { BuiltinThemeType } from '@vben-core/typings';
|
||||
|
||||
import type { SupportedLanguagesType } from './types';
|
||||
|
||||
interface Language {
|
||||
@@ -5,17 +7,17 @@ interface Language {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export const COLOR_PRIMARY_RESETS = [
|
||||
'hsl(211 91% 39%)',
|
||||
'hsl(212 100% 45%)',
|
||||
'hsl(181 84% 32%)',
|
||||
'hsl(161 90% 43%)',
|
||||
'hsl(231 98% 65%)',
|
||||
'hsl(245 82% 67%)',
|
||||
'hsl(347 77% 60%)',
|
||||
];
|
||||
interface BuiltinThemePreset {
|
||||
color: string;
|
||||
darkPrimaryColor?: string;
|
||||
primaryColor?: string;
|
||||
type: BuiltinThemeType;
|
||||
}
|
||||
|
||||
export const SUPPORT_LANGUAGES: Language[] = [
|
||||
/**
|
||||
* Supported languages
|
||||
*/
|
||||
const SUPPORT_LANGUAGES: Language[] = [
|
||||
{
|
||||
key: 'zh-CN',
|
||||
text: '简体中文',
|
||||
@@ -25,3 +27,80 @@ export const SUPPORT_LANGUAGES: Language[] = [
|
||||
text: 'English',
|
||||
},
|
||||
];
|
||||
|
||||
const BUILT_IN_THEME_PRESETS: BuiltinThemePreset[] = [
|
||||
{
|
||||
color: 'hsl(245 82% 67%)',
|
||||
type: 'default',
|
||||
},
|
||||
{
|
||||
color: 'hsl(231 98% 65%)',
|
||||
type: 'violet',
|
||||
},
|
||||
{
|
||||
color: 'hsl(347 77% 60%)',
|
||||
type: 'pink',
|
||||
},
|
||||
{
|
||||
color: 'hsl(0 75% 42%)',
|
||||
type: 'rose',
|
||||
},
|
||||
{
|
||||
color: 'hsl(212 100% 45%)',
|
||||
type: 'sky-blue',
|
||||
},
|
||||
{
|
||||
color: 'hsl(211 91% 39%)',
|
||||
type: 'deep-blue',
|
||||
},
|
||||
{
|
||||
color: 'hsl(161 90% 43%)',
|
||||
type: 'green',
|
||||
},
|
||||
{
|
||||
color: 'hsl(181 84% 32%)',
|
||||
type: 'deep-green',
|
||||
},
|
||||
{
|
||||
color: 'hsl(18 89% 40%)',
|
||||
type: 'orange',
|
||||
},
|
||||
{
|
||||
color: 'hsl(42 84% 61%)',
|
||||
type: 'yellow',
|
||||
},
|
||||
{
|
||||
color: 'hsl(240 5% 26%)',
|
||||
darkPrimaryColor: 'hsl(0 0 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'zinc',
|
||||
},
|
||||
{
|
||||
color: 'hsl(0 0% 25%)',
|
||||
darkPrimaryColor: 'hsl(0 0 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'neutral',
|
||||
},
|
||||
{
|
||||
color: 'hsl(215 25% 27%)',
|
||||
darkPrimaryColor: 'hsl(0 0 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'slate',
|
||||
},
|
||||
{
|
||||
color: 'hsl(217 19% 27%)',
|
||||
darkPrimaryColor: 'hsl(0 0 98%)',
|
||||
primaryColor: 'hsl(240 5.9% 10%)',
|
||||
type: 'gray',
|
||||
},
|
||||
{
|
||||
color: '',
|
||||
type: 'custom',
|
||||
},
|
||||
];
|
||||
|
||||
export const COLOR_PRESETS = [...BUILT_IN_THEME_PRESETS].slice(0, 7);
|
||||
|
||||
export { BUILT_IN_THEME_PRESETS, SUPPORT_LANGUAGES };
|
||||
|
||||
export type { BuiltinThemePreset };
|
||||
|
@@ -55,7 +55,6 @@ describe('preferences', () => {
|
||||
const overrides: any = {
|
||||
app: {
|
||||
locale: 'en-US',
|
||||
themeMode: 'light',
|
||||
},
|
||||
};
|
||||
await preferenceManager.initPreferences({
|
||||
@@ -79,10 +78,12 @@ describe('preferences', () => {
|
||||
|
||||
it('updates theme mode correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
app: { themeMode: 'light' },
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.themeMode).toBe('light');
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
|
||||
it('updates color modes correctly', () => {
|
||||
@@ -97,7 +98,9 @@ describe('preferences', () => {
|
||||
it('resets preferences to default', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
app: { themeMode: 'light' },
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
@@ -146,10 +149,10 @@ describe('preferences', () => {
|
||||
});
|
||||
it('updates the sidebar collapse state correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
sidebar: { collapse: true },
|
||||
sidebar: { collapsed: true },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().sidebar.collapse).toBe(true);
|
||||
expect(preferenceManager.getPreferences().sidebar.collapsed).toBe(true);
|
||||
});
|
||||
it('updates the navigation style type correctly', () => {
|
||||
preferenceManager.updatePreferences({
|
||||
@@ -164,8 +167,11 @@ describe('preferences', () => {
|
||||
it('resets preferences to default correctly', () => {
|
||||
// 先更新一些偏好设置
|
||||
preferenceManager.updatePreferences({
|
||||
app: { locale: 'en-US', themeMode: 'light' },
|
||||
sidebar: { collapse: true, width: 200 },
|
||||
app: { locale: 'en-US' },
|
||||
sidebar: { collapsed: true, width: 200 },
|
||||
theme: {
|
||||
mode: 'light',
|
||||
},
|
||||
});
|
||||
|
||||
// 然后重置偏好设置
|
||||
@@ -232,10 +238,10 @@ describe('preferences', () => {
|
||||
await preferenceManager.initPreferences(overrides);
|
||||
|
||||
preferenceManager.updatePreferences({
|
||||
app: { themeMode: 'light' },
|
||||
theme: { mode: 'light' },
|
||||
});
|
||||
|
||||
expect(preferenceManager.getPreferences().app.themeMode).toBe('light');
|
||||
expect(preferenceManager.getPreferences().theme.mode).toBe('light');
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -5,16 +5,17 @@ import type { Preferences } from './types';
|
||||
import { markRaw, reactive, readonly, watch } from 'vue';
|
||||
|
||||
import { StorageManager } from '@vben-core/cache';
|
||||
import { convertToHslCssVar, merge } from '@vben-core/toolkit';
|
||||
import { generatorColorVariables } from '@vben-core/colorful';
|
||||
import { merge, updateCSSVariables } from '@vben-core/toolkit';
|
||||
|
||||
import {
|
||||
breakpointsTailwind,
|
||||
useBreakpoints,
|
||||
useCssVar,
|
||||
useDebounceFn,
|
||||
} from '@vueuse/core';
|
||||
|
||||
import { defaultPreferences } from './config';
|
||||
import { BUILT_IN_THEME_PRESETS } from './constants';
|
||||
|
||||
const STORAGE_KEY = 'preferences';
|
||||
const STORAGE_KEY_LOCALE = `${STORAGE_KEY}-locale`;
|
||||
@@ -59,7 +60,7 @@ class PreferenceManager {
|
||||
private _savePreferences(preference: Preferences) {
|
||||
this.cache?.setItem(STORAGE_KEY, preference);
|
||||
this.cache?.setItem(STORAGE_KEY_LOCALE, preference.app.locale);
|
||||
this.cache?.setItem(STORAGE_KEY_THEME, preference.app.themeMode);
|
||||
this.cache?.setItem(STORAGE_KEY_THEME, preference.theme.mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,11 +73,7 @@ class PreferenceManager {
|
||||
const themeUpdates = updates.theme || {};
|
||||
const appUpdates = updates.app || {};
|
||||
|
||||
if (themeUpdates.colorPrimary) {
|
||||
this.updateCssVar(this.state);
|
||||
}
|
||||
|
||||
if (appUpdates.themeMode) {
|
||||
if (themeUpdates && Object.keys(themeUpdates).length > 0) {
|
||||
this.updateTheme(this.state);
|
||||
}
|
||||
|
||||
@@ -149,7 +146,7 @@ class PreferenceManager {
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', ({ matches: isDark }) => {
|
||||
this.updatePreferences({
|
||||
app: { themeMode: isDark ? 'dark' : 'light' },
|
||||
theme: { mode: isDark ? 'dark' : 'light' },
|
||||
});
|
||||
this.updateTheme(this.state);
|
||||
});
|
||||
@@ -178,15 +175,37 @@ class PreferenceManager {
|
||||
* 更新 CSS 变量
|
||||
* @param preference - 当前偏好设置对象,它的颜色值将被转换成 HSL 格式并设置为 CSS 变量。
|
||||
*/
|
||||
private updateCssVar(preference: Preferences) {
|
||||
if (preference.theme) {
|
||||
for (const [key, value] of Object.entries(preference.theme)) {
|
||||
if (['colorPrimary'].includes(key)) {
|
||||
const cssVarValue = useCssVar(`--primary`);
|
||||
cssVarValue.value = convertToHslCssVar(value);
|
||||
}
|
||||
}
|
||||
private updateMainColors(preference: Preferences) {
|
||||
if (!preference.theme) {
|
||||
return;
|
||||
}
|
||||
const { colorDestructive, colorPrimary, colorSuccess, colorWarning } =
|
||||
preference.theme;
|
||||
|
||||
const colorVariables = generatorColorVariables([
|
||||
{ color: colorPrimary, name: 'primary' },
|
||||
{ alias: 'warning', color: colorWarning, name: 'yellow' },
|
||||
{ alias: 'success', color: colorSuccess, name: 'green' },
|
||||
{ alias: 'destructive', color: colorDestructive, name: 'red' },
|
||||
]);
|
||||
|
||||
if (colorPrimary) {
|
||||
document.documentElement.style.setProperty(
|
||||
'--primary',
|
||||
colorVariables['--primary-600'],
|
||||
);
|
||||
}
|
||||
|
||||
if (colorVariables['--green-600']) {
|
||||
colorVariables['--success'] = colorVariables['--green-600'];
|
||||
}
|
||||
if (colorVariables['--yellow-600']) {
|
||||
colorVariables['--warning'] = colorVariables['--yellow-600'];
|
||||
}
|
||||
if (colorVariables['--red-600']) {
|
||||
colorVariables['--destructive'] = colorVariables['--red-600'];
|
||||
}
|
||||
updateCSSVariables(colorVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,14 +225,61 @@ class PreferenceManager {
|
||||
private updateTheme(preferences: Preferences) {
|
||||
// 当修改到颜色变量时,更新 css 变量
|
||||
const root = document.documentElement;
|
||||
if (root) {
|
||||
const themeMode = preferences?.app?.themeMode;
|
||||
if (!themeMode) {
|
||||
return;
|
||||
}
|
||||
const dark = isDarkTheme(themeMode);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
builtinType,
|
||||
colorDestructive,
|
||||
colorPrimary,
|
||||
colorSuccess,
|
||||
colorWarning,
|
||||
mode,
|
||||
radius,
|
||||
} = preferences?.theme ?? {};
|
||||
|
||||
if (mode) {
|
||||
const dark = isDarkTheme(mode);
|
||||
root.classList.toggle('dark', dark);
|
||||
}
|
||||
|
||||
if (builtinType) {
|
||||
const rootTheme = root.dataset.theme;
|
||||
if (rootTheme !== builtinType) {
|
||||
root.dataset.theme = builtinType;
|
||||
}
|
||||
}
|
||||
|
||||
const currentBuiltType = BUILT_IN_THEME_PRESETS.find(
|
||||
(item) => item.type === builtinType,
|
||||
);
|
||||
|
||||
let builtinTypeColorPrimary: string | undefined = '';
|
||||
|
||||
if (currentBuiltType) {
|
||||
const isDark = isDarkTheme(this.state.theme.mode);
|
||||
|
||||
const color = isDark
|
||||
? currentBuiltType.darkPrimaryColor || currentBuiltType.primaryColor
|
||||
: currentBuiltType.primaryColor;
|
||||
builtinTypeColorPrimary = color || currentBuiltType.color;
|
||||
}
|
||||
|
||||
if (
|
||||
builtinTypeColorPrimary ||
|
||||
colorPrimary ||
|
||||
colorDestructive ||
|
||||
colorSuccess ||
|
||||
colorWarning
|
||||
) {
|
||||
preferences.theme.colorPrimary = builtinTypeColorPrimary || colorPrimary;
|
||||
this.updateMainColors(preferences);
|
||||
}
|
||||
|
||||
if (radius) {
|
||||
document.documentElement.style.setProperty('--radius', `${radius}rem`);
|
||||
}
|
||||
}
|
||||
|
||||
// public getFlatPreferences() {
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
BuiltinThemeType,
|
||||
ContentCompactType,
|
||||
LayoutHeaderModeType,
|
||||
LayoutType,
|
||||
@@ -45,8 +46,6 @@ interface AppPreferences {
|
||||
semiDarkMenu: boolean;
|
||||
/** 是否显示偏好设置 */
|
||||
showPreference: boolean;
|
||||
/** 当前主题 */
|
||||
themeMode: ThemeModeType;
|
||||
}
|
||||
|
||||
interface BreadcrumbPreferences {
|
||||
@@ -132,8 +131,20 @@ interface TabbarPreferences {
|
||||
}
|
||||
|
||||
interface ThemePreferences {
|
||||
/** 内置主题名 */
|
||||
builtinType: BuiltinThemeType;
|
||||
/** 错误色 */
|
||||
colorDestructive: string;
|
||||
/** 主题色 */
|
||||
colorPrimary: string;
|
||||
/** 成功色 */
|
||||
colorSuccess: string;
|
||||
/** 警告色 */
|
||||
colorWarning: string;
|
||||
/** 当前主题 */
|
||||
mode: ThemeModeType;
|
||||
/** 圆角 */
|
||||
radius: string;
|
||||
}
|
||||
|
||||
interface TransitionPreferences {
|
||||
|
@@ -24,7 +24,7 @@ function usePreferences() {
|
||||
* @returns 如果主题为暗黑模式,返回 true,否则返回 false。
|
||||
*/
|
||||
const isDark = computed(() => {
|
||||
return isDarkTheme(appPreferences.value.themeMode);
|
||||
return isDarkTheme(preferences.theme.mode);
|
||||
});
|
||||
|
||||
const theme = computed(() => {
|
||||
|
7
packages/@core/shared/colorful/build.config.ts
Normal file
7
packages/@core/shared/colorful/build.config.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { defineBuildConfig } from 'unbuild';
|
||||
|
||||
export default defineBuildConfig({
|
||||
clean: true,
|
||||
declaration: true,
|
||||
entries: ['src/index'],
|
||||
});
|
42
packages/@core/shared/colorful/package.json
Normal file
42
packages/@core/shared/colorful/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@vben-core/colorful",
|
||||
"version": "5.0.0",
|
||||
"homepage": "https://github.com/vbenjs/vue-vben-admin",
|
||||
"bugs": "https://github.com/vbenjs/vue-vben-admin/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vbenjs/vue-vben-admin.git",
|
||||
"directory": "packages/@vben-core/shared/colorful"
|
||||
},
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "pnpm unbuild",
|
||||
"stub": "pnpm unbuild --stub"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.mjs",
|
||||
"module": "./dist/index.mjs",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./src/index.ts",
|
||||
"development": "./src/index.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"default": "./dist/index.mjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/colors": "^7.0.2",
|
||||
"@ctrl/tinycolor": "4.1.0"
|
||||
}
|
||||
}
|
45
packages/@core/shared/colorful/src/generator.ts
Normal file
45
packages/@core/shared/colorful/src/generator.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { generate } from '@ant-design/colors';
|
||||
|
||||
import { convertToHslCssVar } from './utils';
|
||||
|
||||
export * from '@ant-design/colors';
|
||||
|
||||
interface Opts {
|
||||
backgroundColor?: string;
|
||||
theme?: 'dark' | 'default';
|
||||
}
|
||||
|
||||
interface ColorItem {
|
||||
alias?: string;
|
||||
color: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
function generatorColorVariables(colorItems: ColorItem[], opts?: Opts) {
|
||||
const colorVariables: Record<string, string> = {};
|
||||
|
||||
colorItems.forEach(({ alias, color, name }) => {
|
||||
if (color) {
|
||||
const colors = generate(color, opts);
|
||||
let mainColor = colors[5];
|
||||
colors.forEach((colorValue, colorIndex) => {
|
||||
const hslColor = convertToHslCssVar(colorValue);
|
||||
colorVariables[`--${name}-${colorIndex + 1}00`] = hslColor;
|
||||
if (alias) {
|
||||
colorVariables[`--${alias}-${colorIndex + 1}00`] = hslColor;
|
||||
}
|
||||
|
||||
if (colorIndex === 5) {
|
||||
mainColor = hslColor;
|
||||
}
|
||||
});
|
||||
if (alias) {
|
||||
colorVariables[`--${alias}`] = mainColor;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return colorVariables;
|
||||
}
|
||||
|
||||
export { generatorColorVariables };
|
2
packages/@core/shared/colorful/src/index.ts
Normal file
2
packages/@core/shared/colorful/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './generator';
|
||||
export * from './utils';
|
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { convertToHsl, convertToHslCssVar, isValidColor } from './color';
|
||||
import { convertToHsl, convertToHslCssVar, isValidColor } from './utils';
|
||||
|
||||
describe('color conversion functions', () => {
|
||||
it('should correctly convert color to HSL format', () => {
|
6
packages/@core/shared/colorful/tsconfig.json
Normal file
6
packages/@core/shared/colorful/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@vben/tsconfig/library.json",
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
@@ -1,4 +1,6 @@
|
||||
:root.dark {
|
||||
:root.dark,
|
||||
:root.dark[data-theme='custom'],
|
||||
:root.dark[data-theme='default'] {
|
||||
/* Default background color of <body />...etc */
|
||||
--background: 220deg 13.04% 8%;
|
||||
--foreground: 220 13% 91%;
|
||||
@@ -16,18 +18,22 @@
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
|
||||
/* 主题颜色 */
|
||||
--primary: 211 91% 39%;
|
||||
|
||||
/* --primary: 245 82% 67%; */
|
||||
--primary-foreground: 0 0 98%;
|
||||
|
||||
/* Used for destructive actions such as <Button variant="destructive"> */
|
||||
--destructive: 0 63% 31%;
|
||||
|
||||
--destructive: 0 78% 68%;
|
||||
--destructive-foreground: 0 0 98%;
|
||||
|
||||
/* Used for success actions such as <message> */
|
||||
|
||||
--success: 144 57% 58%;
|
||||
--success-foreground: 0 0 98%;
|
||||
|
||||
/* Used for warning actions such as <message> */
|
||||
|
||||
--warning: 42 84% 61%;
|
||||
--warning-foreground: 0 0 98%;
|
||||
|
||||
@@ -64,5 +70,283 @@
|
||||
/* 基本圆角大小 */
|
||||
--radius: 0.5rem;
|
||||
|
||||
/* =============component & UI============= */
|
||||
|
||||
/* authentication */
|
||||
--authentication: 240deg 11% 2%;
|
||||
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='violet'] {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--card: 224 71.4% 4.1%;
|
||||
--card-foreground: 210 20% 98%;
|
||||
--popover: 224 71.4% 4.1%;
|
||||
--popover-foreground: 210 20% 98%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 215 27.9% 16.9%;
|
||||
--secondary-foreground: 210 20% 98%;
|
||||
--muted: 215 27.9% 16.9%;
|
||||
--muted-foreground: 217.9 10.6% 64.9%;
|
||||
--accent: 215 27.9% 16.9%;
|
||||
--accent-foreground: 210 20% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 263.4 70% 50.4%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='pink'] {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 0 0% 95%;
|
||||
--card: 24 9.8% 10%;
|
||||
--card-foreground: 0 0% 95%;
|
||||
--popover: 0 0% 9%;
|
||||
--popover-foreground: 0 0% 95%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 15%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 346.8 77.2% 49.8%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='rose'] {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary-foreground: 0 85.7% 97.3%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
--ring: 0 72.2% 50.6%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='sky-blue'] {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 224.3 76.3% 48%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='deep-blue'] {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 224.3 76.3% 48%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='green'] {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 0 0% 95%;
|
||||
--card: 24 9.8% 10%;
|
||||
--card-foreground: 0 0% 95%;
|
||||
--popover: 0 0% 9%;
|
||||
--popover-foreground: 0 0% 95%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 15%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 142.4 71.8% 29.2%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='deep-green'] {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 0 0% 95%;
|
||||
--card: 24 9.8% 10%;
|
||||
--card-foreground: 0 0% 95%;
|
||||
--popover: 0 0% 9%;
|
||||
--popover-foreground: 0 0% 95%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 15%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 85.7% 97.3%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 142.4 71.8% 29.2%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='orange'] {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 60 9.1% 97.8%;
|
||||
--card: 20 14.3% 4.1%;
|
||||
--card-foreground: 60 9.1% 97.8%;
|
||||
--popover: 20 14.3% 4.1%;
|
||||
--popover-foreground: 60 9.1% 97.8%;
|
||||
--primary-foreground: 60 9.1% 97.8%;
|
||||
--secondary: 12 6.5% 15.1%;
|
||||
--secondary-foreground: 60 9.1% 97.8%;
|
||||
--muted: 12 6.5% 15.1%;
|
||||
--muted-foreground: 24 5.4% 63.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 60 9.1% 97.8%;
|
||||
--destructive: 0 72.2% 50.6%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
--border: 12 6.5% 15.1%;
|
||||
--input: 12 6.5% 15.1%;
|
||||
--ring: 20.5 90.2% 48.2%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='yellow'] {
|
||||
--background: 20 14.3% 4.1%;
|
||||
--foreground: 60 9.1% 97.8%;
|
||||
--card: 20 14.3% 4.1%;
|
||||
--card-foreground: 60 9.1% 97.8%;
|
||||
--popover: 20 14.3% 4.1%;
|
||||
--popover-foreground: 60 9.1% 97.8%;
|
||||
--primary-foreground: 26 83.3% 14.1%;
|
||||
--secondary: 12 6.5% 15.1%;
|
||||
--secondary-foreground: 60 9.1% 97.8%;
|
||||
--muted: 12 6.5% 15.1%;
|
||||
--muted-foreground: 24 5.4% 63.9%;
|
||||
--accent: 12 6.5% 15.1%;
|
||||
--accent-foreground: 60 9.1% 97.8%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
--border: 12 6.5% 15.1%;
|
||||
--input: 12 6.5% 15.1%;
|
||||
--ring: 35.5 91.7% 32.9%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='zinc'] {
|
||||
--background: 240 10% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 240 10% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 240 10% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary-foreground: 240 5.9% 10%;
|
||||
--secondary: 240 3.7% 15.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 240 3.7% 15.9%;
|
||||
--muted-foreground: 240 5% 64.9%;
|
||||
--accent: 240 3.7% 15.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 3.7% 15.9%;
|
||||
--input: 240 3.7% 15.9%;
|
||||
--ring: 240 4.9% 83.9%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='neutral'] {
|
||||
--background: 0 0% 3.9%;
|
||||
--foreground: 0 0% 98%;
|
||||
--card: 0 0% 3.9%;
|
||||
--card-foreground: 0 0% 98%;
|
||||
--popover: 0 0% 3.9%;
|
||||
--popover-foreground: 0 0% 98%;
|
||||
--primary-foreground: 0 0% 9%;
|
||||
--secondary: 0 0% 14.9%;
|
||||
--secondary-foreground: 0 0% 98%;
|
||||
--muted: 0 0% 14.9%;
|
||||
--muted-foreground: 0 0% 63.9%;
|
||||
--accent: 0 0% 14.9%;
|
||||
--accent-foreground: 0 0% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 14.9%;
|
||||
--input: 0 0% 14.9%;
|
||||
--ring: 0 0% 83.1%;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='slate'] {
|
||||
--background: 222.2 84% 4.9%;
|
||||
--foreground: 210 40% 98%;
|
||||
--card: 222.2 84% 4.9%;
|
||||
--card-foreground: 210 40% 98%;
|
||||
--popover: 222.2 84% 4.9%;
|
||||
--popover-foreground: 210 40% 98%;
|
||||
--primary-foreground: 222.2 47.4% 11.2%;
|
||||
--secondary: 217.2 32.6% 17.5%;
|
||||
--secondary-foreground: 210 40% 98%;
|
||||
--muted: 217.2 32.6% 17.5%;
|
||||
--muted-foreground: 215 20.2% 65.1%;
|
||||
--accent: 217.2 32.6% 17.5%;
|
||||
--accent-foreground: 210 40% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 217.2 32.6% 17.5%;
|
||||
--input: 217.2 32.6% 17.5%;
|
||||
--ring: 212.7 26.8% 83.9;
|
||||
}
|
||||
|
||||
:root.dark[data-theme='gray'] {
|
||||
--background: 224 71.4% 4.1%;
|
||||
--foreground: 210 20% 98%;
|
||||
--card: 224 71.4% 4.1%;
|
||||
--card-foreground: 210 20% 98%;
|
||||
--popover: 224 71.4% 4.1%;
|
||||
--popover-foreground: 210 20% 98%;
|
||||
--primary-foreground: 220.9 39.3% 11%;
|
||||
--secondary: 215 27.9% 16.9%;
|
||||
--secondary-foreground: 210 20% 98%;
|
||||
--muted: 215 27.9% 16.9%;
|
||||
--muted-foreground: 217.9 10.6% 64.9%;
|
||||
--accent: 215 27.9% 16.9%;
|
||||
--accent-foreground: 210 20% 98%;
|
||||
--destructive: 0 62.8% 30.6%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 215 27.9% 16.9%;
|
||||
--input: 215 27.9% 16.9%;
|
||||
--ring: 216 12.2% 83.9%;
|
||||
}
|
||||
|
@@ -17,22 +17,27 @@
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
|
||||
/* 主题颜色 */
|
||||
|
||||
--primary: 211 91% 39%;
|
||||
--primary-foreground: 0 0 98%;
|
||||
|
||||
/* Used for destructive actions such as <Button variant="destructive"> */
|
||||
|
||||
--destructive: 0 78% 68%;
|
||||
--destructive-foreground: 0 0 98%;
|
||||
|
||||
/* Used for success actions such as <message> */
|
||||
|
||||
--success: 144 57% 58%;
|
||||
--success-foreground: 0 0 98%;
|
||||
|
||||
/* Used for warning actions such as <message> */
|
||||
|
||||
--warning: 42 84% 61%;
|
||||
--warning-foreground: 0 0 98%;
|
||||
|
||||
/* Secondary colors for <Button /> */
|
||||
|
||||
--secondary: 240 5% 96%;
|
||||
--secondary-foreground: 240 6% 10%;
|
||||
|
||||
@@ -73,6 +78,9 @@
|
||||
|
||||
/* =============component & UI============= */
|
||||
|
||||
/* authentication */
|
||||
--authentication: 231deg 61% 44%;
|
||||
|
||||
/* menu */
|
||||
--menu: 0deg 0% 100%;
|
||||
--menu-darken: 0deg 0% 95%;
|
||||
@@ -84,3 +92,276 @@
|
||||
accent-color: var(--primary);
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
:root[data-theme='violet'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 71.4% 4.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 71.4% 4.1%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 220 14.3% 95.9%;
|
||||
--secondary-foreground: 220.9 39.3% 11%;
|
||||
--muted: 220 14.3% 95.9%;
|
||||
--muted-foreground: 220 8.9% 46.1%;
|
||||
--accent: 220 14.3% 95.9%;
|
||||
--accent-foreground: 220.9 39.3% 11%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 262.1 83.3% 57.8%;
|
||||
}
|
||||
|
||||
:root[data-theme='pink'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 346.8 77.2% 49.8%;
|
||||
}
|
||||
|
||||
:root[data-theme='rose'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 346.8 77.2% 49.8%;
|
||||
}
|
||||
|
||||
:root[data-theme='sky-blue'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
}
|
||||
|
||||
:root[data-theme='deep-blue'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 221.2 83.2% 53.3%;
|
||||
}
|
||||
|
||||
:root[data-theme='green'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 142.1 76.2% 36.3%;
|
||||
}
|
||||
|
||||
:root[data-theme='deep-green'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary-foreground: 355.7 100% 97.3%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 142.1 76.2% 36.3%;
|
||||
}
|
||||
|
||||
:root[data-theme='orange'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 20 14.3% 4.1%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 20 14.3% 4.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 20 14.3% 4.1%;
|
||||
--primary-foreground: 60 9.1% 97.8%;
|
||||
--secondary: 60 4.8% 95.9%;
|
||||
--secondary-foreground: 24 9.8% 10%;
|
||||
--muted: 60 4.8% 95.9%;
|
||||
--muted-foreground: 25 5.3% 44.7%;
|
||||
--accent: 60 4.8% 95.9%;
|
||||
--accent-foreground: 24 9.8% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
--border: 20 5.9% 90%;
|
||||
--input: 20 5.9% 90%;
|
||||
--ring: 24.6 95% 53.1%;
|
||||
}
|
||||
|
||||
:root[data-theme='yellow'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 20 14.3% 4.1%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 20 14.3% 4.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 20 14.3% 4.1%;
|
||||
--primary-foreground: 26 83.3% 14.1%;
|
||||
--secondary: 60 4.8% 95.9%;
|
||||
--secondary-foreground: 24 9.8% 10%;
|
||||
--muted: 60 4.8% 95.9%;
|
||||
--muted-foreground: 25 5.3% 44.7%;
|
||||
--accent: 60 4.8% 95.9%;
|
||||
--accent-foreground: 24 9.8% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 60 9.1% 97.8%;
|
||||
--border: 20 5.9% 90%;
|
||||
--input: 20 5.9% 90%;
|
||||
--ring: 20 14.3% 4.1%;
|
||||
}
|
||||
|
||||
:root[data-theme='zinc'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 240 10% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 240 10% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 240 10% 3.9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 240 4.8% 95.9%;
|
||||
--secondary-foreground: 240 5.9% 10%;
|
||||
--muted: 240 4.8% 95.9%;
|
||||
--muted-foreground: 240 3.8% 46.1%;
|
||||
--accent: 240 4.8% 95.9%;
|
||||
--accent-foreground: 240 5.9% 10%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 240 5.9% 90%;
|
||||
--input: 240 5.9% 90%;
|
||||
--ring: 240 5.9% 10%;
|
||||
}
|
||||
|
||||
:root[data-theme='neutral'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 0 0% 3.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 0 0% 3.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 0 0% 3.9%;
|
||||
--primary-foreground: 0 0% 98%;
|
||||
--secondary: 0 0% 96.1%;
|
||||
--secondary-foreground: 0 0% 9%;
|
||||
--muted: 0 0% 96.1%;
|
||||
--muted-foreground: 0 0% 45.1%;
|
||||
--accent: 0 0% 96.1%;
|
||||
--accent-foreground: 0 0% 9%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 0 0% 98%;
|
||||
--border: 0 0% 89.8%;
|
||||
--input: 0 0% 89.8%;
|
||||
--ring: 0 0% 3.9%;
|
||||
}
|
||||
|
||||
:root[data-theme='slate'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 222.2 84% 4.9%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 222.2 84% 4.9%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222.2 84% 4.9%;
|
||||
--primary-foreground: 210 40% 98%;
|
||||
--secondary: 210 40% 96.1%;
|
||||
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||
--muted: 210 40% 96.1%;
|
||||
--muted-foreground: 215.4 16.3% 46.9%;
|
||||
--accent: 210 40% 96.1%;
|
||||
--accent-foreground: 222.2 47.4% 11.2%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 214.3 31.8% 91.4%;
|
||||
--input: 214.3 31.8% 91.4%;
|
||||
--ring: 222.2 84% 4.9%;
|
||||
}
|
||||
|
||||
:root[data-theme='gray'] {
|
||||
--background: 0 0% 100%;
|
||||
--foreground: 224 71.4% 4.1%;
|
||||
--card: 0 0% 100%;
|
||||
--card-foreground: 224 71.4% 4.1%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 224 71.4% 4.1%;
|
||||
--primary-foreground: 210 20% 98%;
|
||||
--secondary: 220 14.3% 95.9%;
|
||||
--secondary-foreground: 220.9 39.3% 11%;
|
||||
--muted: 220 14.3% 95.9%;
|
||||
--muted-foreground: 220 8.9% 46.1%;
|
||||
--accent: 220 14.3% 95.9%;
|
||||
--accent-foreground: 220.9 39.3% 11%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 20% 98%;
|
||||
--border: 220 13% 91%;
|
||||
--input: 220 13% 91%;
|
||||
--ring: 224 71.4% 4.1%;
|
||||
}
|
||||
|
@@ -36,7 +36,6 @@
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@ctrl/tinycolor": "4.1.0",
|
||||
"@vue/shared": "^3.4.30",
|
||||
"clsx": "2.1.1",
|
||||
"dayjs": "^1.11.11",
|
||||
|
@@ -1,5 +1,4 @@
|
||||
export * from './cn';
|
||||
export * from './color';
|
||||
export * from './diff';
|
||||
export * from './hash';
|
||||
export * from './inference';
|
||||
@@ -8,4 +7,5 @@ export * from './merge';
|
||||
export * from './namespace';
|
||||
export * from './nprogress';
|
||||
export * from './tree';
|
||||
export * from './update-css-variables';
|
||||
export * from './window';
|
||||
|
@@ -0,0 +1,30 @@
|
||||
import { expect, it } from 'vitest';
|
||||
|
||||
import { updateCSSVariables } from './update-css-variables';
|
||||
|
||||
it('updateCSSVariables should update CSS variables in :root selector', () => {
|
||||
// 模拟初始的内联样式表内容
|
||||
const initialStyleContent = ':root { --primaryColor: red; }';
|
||||
document.head.innerHTML = `<style id="custom-styles">${initialStyleContent}</style>`;
|
||||
|
||||
// 要更新的CSS变量和它们的新值
|
||||
const updatedVariables = {
|
||||
fontSize: '16px',
|
||||
primaryColor: 'blue',
|
||||
secondaryColor: 'green',
|
||||
};
|
||||
|
||||
// 调用函数来更新CSS变量
|
||||
updateCSSVariables(updatedVariables, 'custom-styles');
|
||||
|
||||
// 获取更新后的样式内容
|
||||
const styleElement = document.querySelector('#custom-styles');
|
||||
const updatedStyleContent = styleElement ? styleElement.textContent : '';
|
||||
|
||||
// 检查更新后的样式内容是否包含正确的更新值
|
||||
expect(
|
||||
updatedStyleContent?.includes('primaryColor: blue;') &&
|
||||
updatedStyleContent?.includes('secondaryColor: green;') &&
|
||||
updatedStyleContent?.includes('fontSize: 16px;'),
|
||||
).toBe(true);
|
||||
});
|
35
packages/@core/shared/toolkit/src/update-css-variables.ts
Normal file
35
packages/@core/shared/toolkit/src/update-css-variables.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 更新 CSS 变量的函数
|
||||
* @param variables 要更新的 CSS 变量与其新值的映射
|
||||
*/
|
||||
function updateCSSVariables(
|
||||
variables: { [key: string]: string },
|
||||
id = '__vben-styles__',
|
||||
): void {
|
||||
// 获取或创建内联样式表元素
|
||||
const styleElement =
|
||||
document.querySelector(`#${id}`) || document.createElement('style');
|
||||
|
||||
styleElement.id = id;
|
||||
|
||||
// 构建要更新的 CSS 变量的样式文本
|
||||
let cssText = ':root {';
|
||||
for (const key in variables) {
|
||||
if (Object.prototype.hasOwnProperty.call(variables, key)) {
|
||||
cssText += `${key}: ${variables[key]};`;
|
||||
}
|
||||
}
|
||||
cssText += '}';
|
||||
|
||||
// 将样式文本赋值给内联样式表
|
||||
styleElement.textContent = cssText;
|
||||
|
||||
// 将内联样式表添加到文档头部
|
||||
if (!document.querySelector(`#${id}`)) {
|
||||
setTimeout(() => {
|
||||
document.head.append(styleElement);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export { updateCSSVariables };
|
21
packages/@core/shared/typings/src/app.d.ts
vendored
21
packages/@core/shared/typings/src/app.d.ts
vendored
@@ -9,11 +9,32 @@ type LayoutType =
|
||||
|
||||
type ThemeModeType = 'auto' | 'dark' | 'light';
|
||||
|
||||
type BuiltinThemeType =
|
||||
| 'custom'
|
||||
| 'deep-blue'
|
||||
| 'deep-green'
|
||||
| 'default'
|
||||
| 'default'
|
||||
| 'gray'
|
||||
| 'green'
|
||||
| 'neutral'
|
||||
| 'orange'
|
||||
| 'pink'
|
||||
| 'red'
|
||||
| 'rose'
|
||||
| 'sky-blue'
|
||||
| 'slate'
|
||||
| 'stone'
|
||||
| 'violet'
|
||||
| 'yellow'
|
||||
| 'zinc';
|
||||
|
||||
type ContentCompactType = 'compact' | 'wide';
|
||||
|
||||
type LayoutHeaderModeType = 'auto' | 'auto-scroll' | 'fixed' | 'static';
|
||||
|
||||
export type {
|
||||
BuiltinThemeType,
|
||||
ContentCompactType,
|
||||
LayoutHeaderModeType,
|
||||
LayoutType,
|
||||
|
@@ -43,6 +43,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-icons/vue": "^1.0.0",
|
||||
"@vben-core/colorful": "workspace:*",
|
||||
"@vben-core/iconify": "workspace:*",
|
||||
"@vben-core/toolkit": "workspace:*",
|
||||
"@vben-core/typings": "workspace:*",
|
||||
|
@@ -3,7 +3,7 @@ import type { MenuRecordBadgeRaw } from '@vben-core/typings';
|
||||
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { isValidColor } from '@vben-core/toolkit';
|
||||
import { isValidColor } from '@vben-core/colorful';
|
||||
|
||||
import BadgeDot from './menu-badge-dot.vue';
|
||||
|
||||
|
@@ -11,7 +11,7 @@ export const badgeVariants = cva(
|
||||
default:
|
||||
'border-transparent bg-accent hover:bg-accent text-primary-foreground shadow',
|
||||
destructive:
|
||||
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
|
||||
'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive-hover',
|
||||
outline: 'text-foreground',
|
||||
secondary:
|
||||
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||
|
@@ -19,7 +19,7 @@ export const buttonVariants = cva(
|
||||
default:
|
||||
'bg-primary text-primary-foreground shadow hover:bg-primary/90',
|
||||
destructive:
|
||||
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90',
|
||||
'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive-hover',
|
||||
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
||||
heavy: 'hover:bg-heavy hover:text-heavy-foreground',
|
||||
icon: 'hover:bg-accent hover:text-accent-foreground text-foreground/80',
|
||||
|
Reference in New Issue
Block a user