From d7ca354b870542885896ef9a3b32e6ce3fce28a9 Mon Sep 17 00:00:00 2001
From: tangjinzhou <415800467@qq.com>
Date: Wed, 30 Jul 2025 23:02:30 +0800
Subject: [PATCH] feat: implement theme toggling and enhance Button styles with
new color variables
---
apps/playground/src/App.vue | 14 +-
packages/ui/src/components/button/Button.vue | 10 +-
.../ui/src/components/button/style/index.css | 28 ++--
packages/ui/src/components/theme/Theme.vue | 22 ++++
packages/ui/src/components/theme/hook.ts | 16 +--
packages/ui/src/components/theme/meta.ts | 11 +-
packages/ui/src/style/base.css | 54 ++++----
packages/ui/src/utils/colorAlgorithm.ts | 121 +++++++-----------
8 files changed, 144 insertions(+), 132 deletions(-)
diff --git a/apps/playground/src/App.vue b/apps/playground/src/App.vue
index 4a9e23dab..4d4ebc37c 100644
--- a/apps/playground/src/App.vue
+++ b/apps/playground/src/App.vue
@@ -1,3 +1,15 @@
-
+
+
+
+
+
diff --git a/packages/ui/src/components/button/Button.vue b/packages/ui/src/components/button/Button.vue
index dc55b3a0e..42b0c5e2f 100644
--- a/packages/ui/src/components/button/Button.vue
+++ b/packages/ui/src/components/button/Button.vue
@@ -14,6 +14,7 @@ import { buttonProps, buttonEmits, ButtonSlots } from './meta'
import { getCssVarColor } from '@/utils/colorAlgorithm'
import { useThemeInject } from '../theme/hook'
import LoadingOutlined from '@ant-design/icons-vue/LoadingOutlined'
+import { defaultColor } from '../theme/meta'
const props = defineProps(buttonProps)
@@ -28,7 +29,7 @@ const color = computed(() => {
}
if (props.danger) {
- return theme.dangerColor
+ return 'red'
}
return theme.primaryColor
@@ -46,7 +47,12 @@ const rootClass = computed(() => {
}
})
const cssVars = computed(() => {
- return getCssVarColor(color.value)
+ return color.value.toLowerCase() !== defaultColor.toLowerCase()
+ ? getCssVarColor(color.value, {
+ appearance: theme.appearance,
+ backgroundColor: theme.backgroundColor,
+ })
+ : {}
})
const handleClick = (event: MouseEvent) => {
diff --git a/packages/ui/src/components/button/style/index.css b/packages/ui/src/components/button/style/index.css
index 749fe6199..3c948b2f0 100644
--- a/packages/ui/src/components/button/style/index.css
+++ b/packages/ui/src/components/button/style/index.css
@@ -11,38 +11,40 @@
}
&:where(.ant-btn-solid:not(:disabled)) {
- @apply border-none bg-[var(--accent-color)] text-[var(--accent-color-content)];
- @apply hover:bg-[var(--accent-color-hover)] active:bg-[var(--accent-color-active)];
+ @apply bg-accent text-accent-content border-none;
+ @apply hover:bg-accent-hover active:bg-accent-active;
}
&:where(.ant-btn-outlined:not(:disabled)),
&:where(.ant-btn-dashed:not(:disabled)) {
- @apply border-[var(--accent-color)] bg-transparent text-[var(--accent-color)];
- @apply hover:text-[var(--accent-color-hover)] active:border-[var(--accent-color-active)] active:text-[var(--accent-color-active)];
- @apply border-[var(--accent-color-active)] hover:border-[var(--accent-color-hover)];
+ @apply border-accent text-accent bg-transparent;
+ @apply hover:text-accent-hover active:border-accent-active active:text-accent-active;
+ @apply border-accent-active hover:border-accent-hover;
}
&:where(.ant-btn-text:not(.ant-btn-custom-color):not(:disabled)) {
- @apply border-none bg-transparent text-[var(--neutral-color)];
- @apply hover:bg-[var(--neutral-disabled-bg)];
+ @apply text-neutral border-none bg-transparent;
+ @apply hover:bg-neutral-disabled-bg;
}
&:where(.ant-btn-text.ant-btn-custom-color:not(:disabled)) {
- @apply border-none bg-transparent text-[var(--accent-color)];
- @apply hover:bg-[var(--accent-color-1)] hover:text-[var(--accent-color-hover)];
+ @apply text-accent border-none bg-transparent;
+ @apply hover:bg-accent-1 hover:text-accent-hover;
}
&:where(.ant-btn-link:not(:disabled)) {
- @apply border-none bg-transparent text-[var(--accent-color)] hover:text-[var(--accent-color-hover)];
+ @apply text-accent border-none bg-transparent;
+ @apply hover:text-accent-hover;
}
&:where(.ant-btn-dashed) {
@apply border-dashed;
}
&:where(.ant-btn-filled:not(:disabled)) {
- @apply border-none bg-[var(--accent-color-1)] text-[var(--accent-color)] hover:text-[var(--accent-color-hover)];
- @apply hover:bg-[var(--accent-color-2)] active:bg-[var(--accent-color-3)] active:text-[var(--accent-color-active)];
+ @apply text-accent bg-accent-1 border-none;
+ @apply hover:bg-accent-2 active:bg-accent-3 active:text-accent-active;
+ @apply hover:text-accent-hover;
}
&:where(.ant-btn-disabled) {
@apply cursor-not-allowed;
- @apply border-[var(--neutral-border)] bg-[var(--neutral-disabled-bg)] text-[var(--neutral-disabled)];
+ @apply text-neutral-disabled bg-neutral-disabled-bg border-neutral-border;
}
&:where(.ant-btn-disabled.ant-btn-text),
&:where(.ant-btn-disabled.ant-btn-link) {
diff --git a/packages/ui/src/components/theme/Theme.vue b/packages/ui/src/components/theme/Theme.vue
index 16219aeb1..d19b3f650 100644
--- a/packages/ui/src/components/theme/Theme.vue
+++ b/packages/ui/src/components/theme/Theme.vue
@@ -5,8 +5,30 @@
diff --git a/packages/ui/src/components/theme/hook.ts b/packages/ui/src/components/theme/hook.ts
index 040cdaaae..3941e5850 100644
--- a/packages/ui/src/components/theme/hook.ts
+++ b/packages/ui/src/components/theme/hook.ts
@@ -1,21 +1,17 @@
-import { inject, InjectionKey, provide, Reactive } from 'vue'
+import { inject, InjectionKey, provide } from 'vue'
+import { ThemeProps } from './meta'
-type ThemeType = Reactive<{
- appearance: 'light' | 'dark'
- primaryColor: string
- dangerColor: string
-}>
-
-const ThemeSymbol: InjectionKey = Symbol('theme')
+const ThemeSymbol: InjectionKey = Symbol('theme')
export const useThemeInject = () => {
return inject(ThemeSymbol, {
appearance: 'light',
primaryColor: '#1677ff',
dangerColor: '#ff4d4f',
- })
+ darkBackgroundColor: '#141414',
+ } as ThemeProps)
}
-export const useThemeProvide = (theme: ThemeType) => {
+export const useThemeProvide = (theme: ThemeProps) => {
provide(ThemeSymbol, theme)
}
diff --git a/packages/ui/src/components/theme/meta.ts b/packages/ui/src/components/theme/meta.ts
index 8ae12f91f..afff1aeda 100644
--- a/packages/ui/src/components/theme/meta.ts
+++ b/packages/ui/src/components/theme/meta.ts
@@ -1,5 +1,6 @@
import { PropType, ExtractPublicPropTypes } from 'vue'
+export const defaultColor = '#1677FF'
// Theme Props
export const themeProps = {
/**
@@ -16,15 +17,15 @@ export const themeProps = {
*/
primaryColor: {
type: String,
- default: '#1677FF',
+ default: defaultColor,
},
/**
- * Specifies the danger color of the component
- * @default '#ff4d4f'
+ * Specifies the background color of the component, only used in dark mode
+ * @default '#141414'
*/
- dangerColor: {
+ backgroundColor: {
type: String,
- default: '#ff4d4f',
+ default: '#141414',
},
} as const
diff --git a/packages/ui/src/style/base.css b/packages/ui/src/style/base.css
index 510d18aec..e8032ac81 100644
--- a/packages/ui/src/style/base.css
+++ b/packages/ui/src/style/base.css
@@ -1,29 +1,33 @@
@theme static {
- --color-base-100: #ffffff;
- --color-base-200: #f7f7f7;
- --color-base-300: #ededed;
- --color-base-content: #222222;
- --color-primary: #151415;
- --color-primary-content: #ffffff;
- --color-secondary: #0d58fc;
- --color-secondary-content: #ffffff;
- --color-accent: #0289ff;
+ --color-accent-1: #e6f4ff;
+ --color-accent-2: #bae0ff;
+ --color-accent-3: #91caff;
+ --color-accent-4: #69b1ff;
+ --color-accent-5: #4096ff;
+ --color-accent-6: #1677ff;
+ --color-accent-7: #0958d9;
+ --color-accent-8: #003eb3;
+ --color-accent-9: #002c8c;
+ --color-accent-10: #001d66;
+ --color-accent: #1677ff;
+ --color-accent-hover: #4096ff;
+ --color-accent-active: #0958d9;
--color-accent-content: #ffffff;
- --color-neutral: #666666;
- --color-neutral-content: #ffffff;
- --color-info: #0d58fc;
- --color-info-content: #ffffff;
- --color-success: #00c573;
- --color-success-content: #ffffff;
- --color-warning: #ff9900;
- --color-warning-content: #ffffff;
- --color-error: #ff3333;
- --color-error-content: #ffffff;
- --neutral-color: #000000e0;
- --neutral-secondary: #000000a6;
- --neutral-disabled: #00000040;
- --neutral-border: #d9d9d9;
- --neutral-separator: #0505050f;
- --neutral-bg: #f5f5f5;
+ --color-neutral: #000000e0;
+ --color-neutral-secondary: #000000a6;
+ --color-neutral-disabled: #00000040;
+ --color-neutral-disabled-bg: #0000000a;
+ --color-neutral-border: #d9d9d9;
+ --color-neutral-separator: #0505050f;
+ --color-neutral-bg: #f5f5f5;
+
+ --color-error: #ff4d4f;
+ --color-warning: #faad14;
+ --color-success: #52c41a;
+ --color-info: #1677ff;
+}
+
+.dark-theme {
+ background-color: #141414;
}
diff --git a/packages/ui/src/utils/colorAlgorithm.ts b/packages/ui/src/utils/colorAlgorithm.ts
index 08eb72480..7c7b4af49 100644
--- a/packages/ui/src/utils/colorAlgorithm.ts
+++ b/packages/ui/src/utils/colorAlgorithm.ts
@@ -1,51 +1,41 @@
import { TinyColor } from '@ctrl/tinycolor'
import { generate, presetPalettes, presetDarkPalettes } from '@ant-design/colors'
-export const getAlphaColor = (baseColor: string, alpha: number) =>
- new TinyColor(baseColor).setAlpha(alpha).toRgbString()
-
-export const getSolidColor = (baseColor: string, brightness: number) => {
- const instance = new TinyColor(baseColor)
- return instance.darken(brightness).toHexString()
-}
-
-export const getTintColor = (baseColor: string, tintNumber: number) => {
- return new TinyColor(baseColor).tint(tintNumber).toString()
-}
-
-export const getShadeColor = (baseColor: string, shadeNumber: number) => {
- return new TinyColor(baseColor).shade(shadeNumber).toString()
-}
-
export const getLightNeutralColor = () => {
return {
- '--neutral-color': '#000000e0',
- '--neutral-secondary': '#000000a6',
- '--neutral-disabled': '#00000040',
- '--neutral-disabled-bg': '#0000000a',
- '--neutral-border': '#d9d9d9',
- '--neutral-separator': '#0505050f',
- '--neutral-bg': '#f5f5f5',
+ '--color-neutral': '#000000e0',
+ '--color-neutral-secondary': '#000000a6',
+ '--color-neutral-disabled': '#00000040',
+ '--color-neutral-disabled-bg': '#0000000a',
+ '--color-neutral-border': '#d9d9d9',
+ '--color-neutral-separator': '#0505050f',
+ '--color-neutral-bg': '#f5f5f5',
}
}
export const getDarkNeutralColor = () => {
return {
- '--neutral-color': '#FFFFFFD9',
- '--neutral-secondary': '#FFFFFFA6',
- '--neutral-disabled': '#FFFFFF40',
- '--neutral-disabled-bg': 'rgba(255, 255, 255, 0.08)',
- '--neutral-border': '#424242',
- '--neutral-separator': '#FDFDFD1F',
- '--neutral-bg': '#000000',
+ '--color-neutral': '#FFFFFFD9',
+ '--color-neutral-secondary': '#FFFFFFA6',
+ '--color-neutral-disabled': '#FFFFFF40',
+ '--color-neutral-disabled-bg': 'rgba(255, 255, 255, 0.08)',
+ '--color-neutral-border': '#424242',
+ '--color-neutral-separator': '#FDFDFD1F',
+ '--color-neutral-bg': '#000000',
}
}
+const cacheColors = new Map>()
+
export const getCssVarColor = (
baseColor: string,
- opts?: { appearance: 'light' | 'dark'; backgroundColor: string },
+ opts: { appearance: 'light' | 'dark'; backgroundColor: string },
) => {
- const { appearance = 'light', backgroundColor = '#141414' } = opts || {}
+ const { appearance = 'light', backgroundColor = '#141414' } = opts
+ const cacheKey = `${baseColor}-${appearance}-${backgroundColor}`
+ if (cacheColors.has(cacheKey)) {
+ return cacheColors.get(cacheKey)
+ }
const color = new TinyColor(baseColor)
const preset = appearance === 'dark' ? presetDarkPalettes : presetPalettes
const colors =
@@ -55,50 +45,29 @@ export const getCssVarColor = (
appearance === 'dark' ? { theme: appearance, backgroundColor } : undefined,
)
const accentColor = colors[5]
- return {
- '--accent-color-1': colors[0],
- '--accent-color-2': colors[1],
- '--accent-color-3': colors[2],
- '--accent-color-4': colors[3],
- '--accent-color-5': colors[4],
- '--accent-color-6': colors[5],
- '--accent-color-7': colors[6],
- '--accent-color-8': colors[7],
- '--accent-color-9': colors[8],
- '--accent-color-10': colors[9],
- '--accent-color': accentColor,
- '--accent-color-hover': colors[4],
- '--accent-color-active': colors[5],
- '--accent-color-content': '#ffffff',
- ...(appearance === 'dark' ? getDarkNeutralColor() : getLightNeutralColor()),
- '--bg-color': baseColor,
- '--bg-color-hover': getTintColor(baseColor, 10),
- '--bg-color-active': getTintColor(baseColor, 20),
- '--bg-color-content': '#ffffff',
+ const cssVars = {
+ '--color-accent-1': colors[0],
+ '--color-accent-2': colors[1],
+ '--color-accent-3': colors[2],
+ '--color-accent-4': colors[3],
+ '--color-accent-5': colors[4],
+ '--color-accent-6': colors[5],
+ '--color-accent-7': colors[6],
+ '--color-accent-8': colors[7],
+ '--color-accent-9': colors[8],
+ '--color-accent-10': colors[9],
+ '--color-accent': accentColor,
+ '--color-accent-hover': colors[4],
+ '--color-accent-active': colors[6],
+ '--color-accent-content': '#ffffff',
- '--border-color': baseColor,
- '--border-color-hover': getTintColor(baseColor, 10),
- '--border-color-active': getTintColor(baseColor, 20),
- '--border-color-tint-10': getTintColor(baseColor, 10),
- '--border-color-tint-20': getTintColor(baseColor, 20),
- '--border-color-tint-30': getTintColor(baseColor, 30),
- '--border-color-tint-40': getTintColor(baseColor, 40),
- '--border-color-tint-50': getTintColor(baseColor, 50),
- '--border-color-tint-60': getTintColor(baseColor, 60),
- '--border-color-tint-70': getTintColor(baseColor, 70),
- '--border-color-tint-80': getTintColor(baseColor, 80),
- '--border-color-tint-90': getTintColor(baseColor, 90),
- '--bg-color-tint-10': getTintColor(baseColor, 10),
- '--bg-color-tint-20': getTintColor(baseColor, 20),
- '--bg-color-tint-30': getTintColor(baseColor, 30),
- '--bg-color-tint-40': getTintColor(baseColor, 40),
- '--bg-color-tint-50': getTintColor(baseColor, 50),
- '--bg-color-tint-60': getTintColor(baseColor, 60),
- '--bg-color-tint-70': getTintColor(baseColor, 70),
- '--bg-color-tint-80': getTintColor(baseColor, 80),
- '--bg-color-tint-90': getTintColor(baseColor, 90),
- '--text-color': baseColor,
- '--text-color-hover': getTintColor(baseColor, 10),
- '--text-color-active': getTintColor(baseColor, 20),
+ '--color-error': preset.red[4],
+ '--color-warning': preset.yellow[4],
+ '--color-success': preset.green[4],
+ '--color-info': preset.blue[4],
+
+ ...(appearance === 'dark' ? getDarkNeutralColor() : getLightNeutralColor()),
}
+ cacheColors.set(cacheKey, cssVars)
+ return cssVars
}