From 02b6b38a568e9dcd19e49e2112db575298cdfa1d Mon Sep 17 00:00:00 2001 From: Soryn Date: Wed, 16 Apr 2025 17:50:23 +0300 Subject: [PATCH] docs: change favicon color on theme color change (#3917) --- docs/app/app.vue | 4 +- docs/app/composables/useFaviconFromTheme.ts | 55 +++++++++++++++++++++ docs/app/error.vue | 4 +- 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 docs/app/composables/useFaviconFromTheme.ts diff --git a/docs/app/app.vue b/docs/app/app.vue index db6bec49..be2d15cc 100644 --- a/docs/app/app.vue +++ b/docs/app/app.vue @@ -23,7 +23,7 @@ useHead({ { key: 'theme-color', name: 'theme-color', content: color } ], link: [ - { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }, + // { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' }, { rel: 'canonical', href: `https://ui.nuxt.com${withoutTrailingSlash(route.path)}` } ], style: [ @@ -40,6 +40,8 @@ useServerSeoMeta({ twitterCard: 'summary_large_image' }) +useFaviconFromTheme() + const { frameworks, modules } = useSharedData() const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) diff --git a/docs/app/composables/useFaviconFromTheme.ts b/docs/app/composables/useFaviconFromTheme.ts new file mode 100644 index 00000000..b9881a78 --- /dev/null +++ b/docs/app/composables/useFaviconFromTheme.ts @@ -0,0 +1,55 @@ +import { useColorMode } from '@vueuse/core' +import { onMounted, watch } from 'vue' +import FaviconSvg from 'public/icon.svg?raw' + +export function useFaviconFromTheme() { + const colorMode = useColorMode() + + function generateFaviconSvg(color: string) { + const parser = new DOMParser() + const doc = parser.parseFromString(FaviconSvg, 'image/svg+xml') + const svg = doc.documentElement + + svg.querySelectorAll('path').forEach((path) => { + path.setAttribute('fill', color) + }) + + return new XMLSerializer().serializeToString(svg) + } + + function updateFavicon() { + const root = document.documentElement + const color = getComputedStyle(root).getPropertyValue('--ui-primary').trim() || '#00DC82' + + const svg = generateFaviconSvg(color) + const encoded = `data:image/svg+xml,${encodeURIComponent(svg)}` + + useFavicon(encoded) + } + + function setupMutationObserver() { + const styleTag = document.getElementById('nuxt-ui-colors') + if (!styleTag) return + + const observer = new MutationObserver(() => { + updateFavicon() + }) + + observer.observe(styleTag, { + characterData: true, + subtree: true, + childList: true + }) + } + + onMounted(() => { + watch(colorMode, () => { + updateFavicon() + }, { + immediate: true, + flush: 'post' + }) + + setupMutationObserver() + }) +} diff --git a/docs/app/error.vue b/docs/app/error.vue index d001718a..644eaf74 100644 --- a/docs/app/error.vue +++ b/docs/app/error.vue @@ -26,7 +26,7 @@ useHead({ { key: 'theme-color', name: 'theme-color', content: color } ], link: [ - { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' } + // { rel: 'icon', type: 'image/svg+xml', href: '/icon.svg' } ], style: [ { innerHTML: radius, id: 'nuxt-ui-radius', tagPriority: -2 }, @@ -47,6 +47,8 @@ useServerSeoMeta({ twitterCard: 'summary_large_image' }) +useFaviconFromTheme() + const { frameworks, modules } = useSharedData() const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)