diff --git a/docs/.vuepress/themes/components/CodeViewer.vue b/docs/.vuepress/themes/components/CodeViewer.vue index a2186d6a..2734aee3 100644 --- a/docs/.vuepress/themes/components/CodeViewer.vue +++ b/docs/.vuepress/themes/components/CodeViewer.vue @@ -6,19 +6,19 @@ defineProps<{ diff --git a/docs/.vuepress/themes/components/ThemeColors.vue b/docs/.vuepress/themes/components/ThemeColors.vue new file mode 100644 index 00000000..16db3e78 --- /dev/null +++ b/docs/.vuepress/themes/components/ThemeColors.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/docs/.vuepress/themes/composables/theme-colors.ts b/docs/.vuepress/themes/composables/theme-colors.ts new file mode 100644 index 00000000..3185d0ad --- /dev/null +++ b/docs/.vuepress/themes/composables/theme-colors.ts @@ -0,0 +1,148 @@ +import { type InjectionKey, type Ref, inject, provide, watch } from 'vue' +import { useSessionStorage, useStyleTag } from '@vueuse/core' + +export interface ThemeColor { + name: string + key: string + value: string + desc: string +} +export type ThemeColors = ThemeColor[] +export interface ThemeColorsGroup { + name: string + group: ThemeColors +} + +const DEFAULT_PRESET = { + light: { + '--vp-c-brand-1': '#5086a1', + '--vp-c-brand-2': '#6aa1b7', + '--vp-c-brand-3': '#8cccd5', + '--vp-c-brand-soft': 'rgba(131, 208, 218, 0.314)', + + '--vp-c-text-1': 'rgba(60, 60, 67)', + '--vp-c-text-2': 'rgba(60, 60, 67, 0.78)', + '--vp-c-text-3': 'rgba(60, 60, 67, 0.56)', + + '--vp-c-bg': '#fff', + '--vp-nav-bg-color': '#fff', + '--vp-nav-screen-bg-color': '#fff', + '--vp-local-nav-bg-color': '#fff', + '--vp-sidebar-bg-color': '#f6f6f7', + '--vp-code-block-bg': '#f6f8fa', + }, + dark: { + '--vp-c-brand-1': '#8cccd5', + '--vp-c-brand-2': '#6aa1b7', + '--vp-c-brand-3': '#5086a1', + '--vp-c-brand-soft': 'rgba(131, 208, 218, 0.314)', + + '--vp-c-text-1': 'rgba(255, 255, 245, 0.86)', + '--vp-c-text-2': 'rgba(235, 235, 245, 0.6)', + '--vp-c-text-3': 'rgba(235, 235, 245, 0.38)', + + '--vp-c-bg': '#1b1b1f', + '--vp-nav-bg-color': '#1b1b1f', + '--vp-nav-screen-bg-color': '#1b1b1f', + '--vp-local-nav-bg-color': '#1b1b1f', + '--vp-sidebar-bg-color': '#161618', + '--vp-code-block-bg': '#202127', + }, +} + +const preset: ThemeColorsGroup[] = [ + { + name: '主题色', + group: [ + { name: 'brand-1', key: '--vp-c-brand-1', value: '', desc: '链接颜色、强调色' }, + { name: 'brand-2', key: '--vp-c-brand-2', value: '', desc: '链接、按钮 hover 颜色' }, + { name: 'brand-3', key: '--vp-c-brand-3', value: '', desc: '背景色、边框色' }, + { name: 'brand-soft', key: '--vp-c-brand-soft', value: '', desc: '辅助色' }, + ], + }, + { + name: '文本颜色', + group: [ + { name: 'text-1', key: '--vp-c-text-1', value: '', desc: '主要文本' }, + { name: 'text-2', key: '--vp-c-text-2', value: '', desc: '次要文本' }, + { name: 'text-3', key: '--vp-c-text-3', value: '', desc: '辅助文本' }, + ], + }, + { + name: '背景色', + group: [ + { name: 'bg', key: '--vp-c-bg', value: '', desc: '主体背景' }, + { name: 'nav-bg', key: '--vp-nav-bg-color', value: '', desc: '导航栏背景' }, + { name: 'nav-screen-bg', key: '--vp-nav-screen-bg-color', value: '', desc: '移动端导航栏' }, + { name: 'local-nav-bg', key: '--vp-local-nav-bg-color', value: '', desc: '页面内导航栏' }, + { name: 'sidebar-bg', key: '--vp-sidebar-bg-color', value: '', desc: '侧边栏背景' }, + { name: 'code-block-bg', key: '--vp-code-block-bg', value: '', desc: '代码块背景' }, + ], + }, +] + +const themeColorSymbol: InjectionKey<{ + lightColors: Ref + darkColors: Ref + css: Ref + reset: () => void +}> = Symbol(__VUEPRESS_DEV__ ? 'theme-color' : '') + +export function setupThemeColors() { + const lightColors = useSessionStorage('custom-theme-colors-light', resolveDefaultColors('light')) + const darkColors = useSessionStorage('custom-theme-colors-dark', resolveDefaultColors('dark')) + + const { css, load } = useStyleTag('') + + watch([lightColors, darkColors], () => { + const content = `${resolveContent(lightColors.value, 'light')}\n${resolveContent(darkColors.value, 'dark')}` + css.value = content + load() + }, { deep: true, immediate: true }) + + function resolveContent(colors: ThemeColorsGroup[], type: 'light' | 'dark') { + const name = type === 'light' ? ':root' : '.dark' + let content = `${name} {\n` + colors.forEach(({ name, group }) => { + content += `\n /**\n * ${name}\n * -------------------------------------------------------------------------- */\n\n` + group.forEach((item) => { + const str = ` ${item.key}: ${item.value};` + content += `${str}${' '.repeat(54 - str.length)}/* ${item.desc} */\n` + }) + }) + content += '}\n' + return content + } + + function resolveDefaultColors(type: 'light' | 'dark') { + return preset.map(group => ({ + name: group.name, + group: group.group.map(item => ({ + ...item, + value: DEFAULT_PRESET[type][item.key], + })), + })) + } + + function reset() { + lightColors.value = resolveDefaultColors('light') + darkColors.value = resolveDefaultColors('dark') + } + + provide(themeColorSymbol, { + lightColors, + darkColors, + css, + reset, + }) +} + +export function useThemeColors() { + const result = inject(themeColorSymbol) + + if (!result) { + throw new Error('useThemeColors() can only be used inside `setupThemeColors()`.') + } + + return result +} diff --git a/docs/notes/tools/custom-theme.md b/docs/notes/tools/custom-theme.md new file mode 100644 index 00000000..cfb34c6c --- /dev/null +++ b/docs/notes/tools/custom-theme.md @@ -0,0 +1,26 @@ +--- +title: 主题颜色工具 +icon: unjs:theme-colors +author: pengzhanbo +createTime: 2024/07/29 13:58:29 +permalink: /tools/theme-colors/ +readingTime: false +editLink: false +contributors: false +lastUpdated: false +head: + - + - link + - href: https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/themes/nano.min.css + rel: stylesheet +--- + +::: tip +这个工具可以帮助您快速创建自定义主题颜色。点击下方的按钮,选择您想要的主题颜色进行配置。 + +工具会自动应用您的修改到当前站点,您可以随意切换到任意页面查看效果。 + +本工具只能帮助您简单的创建主题颜色。如果您期望更加复杂的主题颜色配置,请查看 [styles/vars.css](https://github.com/pengzhanbo/vuepress-theme-plume/blob/main/theme/src/client/styles/vars.css) +::: + +