docs: add custom theme colors tools

This commit is contained in:
pengzhanbo 2024-07-31 11:45:54 +08:00
parent 5f6825a645
commit 7a1bc8f73d
5 changed files with 321 additions and 4 deletions

View File

@ -6,19 +6,19 @@ defineProps<{
</script>
<template>
<div :class="`code-viewer language-${lang}`" :data-ext="lang">
<button class="copy-code-button" :data-lang="lang" />
<div :class="`code-viewer language-${lang}`" :data-title="lang">
<button class="copy" :data-lang="lang" title="Copy code" data-copied="已复制" />
<pre class="shiki shiki-themes vitesse-light vitesse-dark vp-code"><code>{{ content }}</code></pre>
</div>
</template>
<style>
.code-viewer .copy-code-button {
.code-viewer .copy {
display: none;
}
@media (min-width: 768px) {
.code-viewer .copy-code-button {
.code-viewer .copy {
display: block;
}
}

View File

@ -0,0 +1,69 @@
<script setup lang="ts">
import 'https://cdn.jsdelivr.net/npm/@simonwep/pickr/dist/pickr.es5.min.js'
import { onMounted, onUnmounted, ref, watch } from 'vue'
const emit = defineEmits<{ (e: 'update:modelValue', value: string): void }>()
// eslint-disable-next-line ts/no-namespace
declare namespace window {
const Pickr: any
}
const color = defineModel<string>('modelValue', { default: 'rgba(0,0,0,1)' })
const pickerEl = ref<HTMLDivElement>()
let picker: any
onMounted(() => {
if (!pickerEl.value || picker)
return
picker = window.Pickr.create({
el: pickerEl.value,
theme: 'nano',
default: color.value,
defaultRepresentation: 'RGBA',
showAlways: false,
components: {
preview: true,
opacity: true,
hue: true,
interaction: { input: true },
},
})
picker.on('change', (hsva) => {
emit('update:modelValue', hsva.toRGBA().toString(0))
})
watch(color, () => {
picker?.setColor(color.value)
})
})
onUnmounted(() => {
picker?.destroyAndRemove()
picker = null
})
</script>
<template>
<div class="color-picker">
<div ref="pickerEl" />
</div>
</template>
<style>
.color-picker {
display: flex;
align-items: center;
justify-content: center;
padding: 4px;
background-color: var(--vp-c-bg-soft);
border: solid 1px var(--vp-c-divider);
border-radius: 32px;
transition: border-color var(--t-color), background-color var(--t-color);
}
.pickr .pcr-button {
overflow: hidden;
border-radius: 50% !important;
}
</style>

View File

@ -0,0 +1,74 @@
<script setup lang="ts">
import VPButton from '@theme/VPButton.vue'
import { useThemeColors } from '../composables/theme-colors.js'
import ColorPick from './ColorPick.vue'
import CodeViewer from './CodeViewer.vue'
const { lightColors, darkColors, css, reset } = useThemeColors()
</script>
<template>
<VPButton theme="alt" text="重置" @click="reset" />
<h2>浅色主题</h2>
<div class="theme-colors-wrapper">
<div v-for="({ name, group }, index) in lightColors" :key="index" class="group">
<h4>{{ name }}</h4>
<section v-for="color in group" :key="color.key" class="theme-color">
<ColorPick v-model="color.value" />
<h5>{{ color.name }}</h5>
<span class="desc">{{ color.desc }}</span>
</section>
</div>
</div>
<h2>深色主题</h2>
<div class="theme-colors-wrapper">
<div v-for="({ name, group }, index) in darkColors" :key="index" class="group">
<h4>{{ name }}</h4>
<section v-for="color in group" :key="color.key" class="theme-color">
<ColorPick v-model="color.value" />
<h5>{{ color.name }}</h5>
<span class="desc">{{ color.desc }}</span>
</section>
</div>
</div>
<p>复制下方的代码到您的项目中请参考 <a href="/custom-style/">主题定制</a> </p>
<CodeViewer :content="css" lang="css" />
</template>
<style scoped>
.theme-colors-wrapper {
display: grid;
gap: 8px 16px;
}
@media (min-width: 640px) {
.theme-colors-wrapper {
grid-template-columns: 1fr 1fr;
}
}
@media (min-width: 1440px) {
.theme-colors-wrapper {
grid-template-columns: 1fr 1fr 1fr;
}
}
.theme-color {
display: flex;
gap: 8px;
align-items: center;
justify-content: flex-start;
margin-bottom: 8px;
}
.theme-color h5 {
margin: 0;
}
.theme-color .desc {
font-size: 14px;
color: var(--vp-c-text-2);
transition: color var(--t-color);
}
</style>

View File

@ -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<ThemeColorsGroup[]>
darkColors: Ref<ThemeColorsGroup[]>
css: Ref<string>
reset: () => void
}> = Symbol(__VUEPRESS_DEV__ ? 'theme-color' : '')
export function setupThemeColors() {
const lightColors = useSessionStorage<ThemeColorsGroup[]>('custom-theme-colors-light', resolveDefaultColors('light'))
const darkColors = useSessionStorage<ThemeColorsGroup[]>('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
}

View File

@ -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)
:::
<ThemeColors />