feat: implement theme toggling and enhance Button styles with new color variables
parent
428fdfc182
commit
d7ca354b87
|
@ -1,3 +1,15 @@
|
|||
<template>
|
||||
<RouterView></RouterView>
|
||||
<button @click="toggleTheme" class="fixed top-2 right-2">toggle {{ appearance }}</button>
|
||||
<a-theme :appearance="appearance">
|
||||
<RouterView />
|
||||
</a-theme>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
|
||||
const appearance = ref('light')
|
||||
|
||||
const toggleTheme = () => {
|
||||
appearance.value = appearance.value === 'light' ? 'dark' : 'light'
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -5,8 +5,30 @@
|
|||
<script setup lang="ts">
|
||||
import { themeProps } from './meta'
|
||||
import { useThemeProvide } from './hook'
|
||||
import { getCssVarColor } from '@/utils/colorAlgorithm'
|
||||
import { watchEffect } from 'vue'
|
||||
|
||||
const props = defineProps(themeProps)
|
||||
|
||||
useThemeProvide(props)
|
||||
|
||||
const style = document.createElement('style')
|
||||
watchEffect(() => {
|
||||
const cssVars = getCssVarColor(props.primaryColor, {
|
||||
appearance: props.appearance,
|
||||
backgroundColor: props.backgroundColor,
|
||||
})
|
||||
document.documentElement.classList.remove('light-theme', 'dark-theme')
|
||||
document.documentElement.classList.add(`${props.appearance}-theme`)
|
||||
style.textContent = `:root.${props.appearance}-theme {
|
||||
${Object.entries(cssVars)
|
||||
.map(([key, value]) => `${key}: ${value};`)
|
||||
.join('\n')}
|
||||
}`
|
||||
document.head.appendChild(style)
|
||||
|
||||
return () => {
|
||||
document.head.removeChild(style)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
|
@ -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<ThemeType> = Symbol('theme')
|
||||
const ThemeSymbol: InjectionKey<ThemeProps> = 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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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<string, Record<string, string>>()
|
||||
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue