diff --git a/src/plugins/plugins.ts b/src/plugins/plugins.ts index 629e2e24a4..98d9186baa 100644 --- a/src/plugins/plugins.ts +++ b/src/plugins/plugins.ts @@ -16,6 +16,7 @@ export default function PluginsPlugin(options: NuxtUIOptions) { const plugins = globSync(['**/*', '!*.d.ts'], { cwd: join(runtimeDir, 'plugins'), absolute: true }) plugins.unshift(resolvePathSync('../runtime/vue/plugins/head', { extensions: ['.ts', '.mjs', '.js'], url: import.meta.url })) + plugins.push(resolvePathSync('../runtime/vue/plugins/colors', { extensions: ['.ts', '.mjs', '.js'], url: import.meta.url })) if (options.colorMode) { plugins.push(resolvePathSync('../runtime/vue/plugins/color-mode', { extensions: ['.ts', '.mjs', '.js'], url: import.meta.url })) } diff --git a/src/runtime/plugins/colors.ts b/src/runtime/plugins/colors.ts index 9ae716d6d6..dd9292ebc5 100644 --- a/src/runtime/plugins/colors.ts +++ b/src/runtime/plugins/colors.ts @@ -1,43 +1,13 @@ import { computed } from 'vue' -import colors from 'tailwindcss/colors' import type { UseHeadInput } from '@unhead/vue/types' import { defineNuxtPlugin, useAppConfig, useNuxtApp, useHead } from '#imports' - -const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const - -function getColor(color: keyof typeof colors, shade: typeof shades[number]): string { - if (color in colors && typeof colors[color] === 'object' && shade in colors[color]) { - return colors[color][shade] as string - } - return '' -} - -function generateShades(key: string, value: string) { - return `${shades.map(shade => `--ui-color-${key}-${shade}: var(--color-${value === 'neutral' ? 'old-neutral' : value}-${shade}, ${getColor(value as keyof typeof colors, shade)});`).join('\n ')}` -} -function generateColor(key: string, shade: number) { - return `--ui-${key}: var(--ui-color-${key}-${shade});` -} +import { generateColorStyles } from '../utils/colors' export default defineNuxtPlugin(() => { const appConfig = useAppConfig() const nuxtApp = useNuxtApp() - const root = computed(() => { - const { neutral, ...colors } = appConfig.ui.colors - - return `@layer base { - :root { - ${Object.entries(appConfig.ui.colors).map(([key, value]: [string, string]) => generateShades(key, value)).join('\n ')} - } - :root, .light { - ${Object.keys(colors).map(key => generateColor(key, 500)).join('\n ')} - } - .dark { - ${Object.keys(colors).map(key => generateColor(key, 400)).join('\n ')} - } -}` - }) + const root = computed(() => generateColorStyles(appConfig.ui.colors)) // Head const headData: UseHeadInput = { diff --git a/src/runtime/utils/colors.ts b/src/runtime/utils/colors.ts new file mode 100644 index 0000000000..f593a8b7e5 --- /dev/null +++ b/src/runtime/utils/colors.ts @@ -0,0 +1,34 @@ +import colors from 'tailwindcss/colors' + +export const shades = [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950] as const + +export function getColor(color: keyof typeof colors, shade: typeof shades[number]): string { + if (color in colors && typeof colors[color] === 'object' && shade in colors[color]) { + return colors[color][shade] as string + } + return '' +} + +export function generateShades(key: string, value: string) { + return `${shades.map(shade => `--ui-color-${key}-${shade}: var(--color-${value === 'neutral' ? 'old-neutral' : value}-${shade}, ${getColor(value as keyof typeof colors, shade)});`).join('\n ')}` +} + +export function generateColor(key: string, shade: number) { + return `--ui-${key}: var(--ui-color-${key}-${shade});` +} + +export function generateColorStyles(colors: Record) { + const { neutral, ...rest } = colors + + return `@layer base { + :root { + ${Object.entries(colors).map(([key, value]: [string, string]) => generateShades(key, value)).join('\n ')} + } + :root, .light { + ${Object.keys(rest).map(key => generateColor(key, 500)).join('\n ')} + } + .dark { + ${Object.keys(rest).map(key => generateColor(key, 400)).join('\n ')} + } +}` +} diff --git a/src/runtime/vue/composables/useAppConfig.ts b/src/runtime/vue/composables/useAppConfig.ts index a06491cfdd..27199fc9ad 100644 --- a/src/runtime/vue/composables/useAppConfig.ts +++ b/src/runtime/vue/composables/useAppConfig.ts @@ -1,3 +1,6 @@ +import { reactive } from 'vue' import appConfig from '#build/app.config' -export const useAppConfig = () => appConfig +const _appConfig = reactive(appConfig) + +export const useAppConfig = () => _appConfig diff --git a/src/runtime/vue/plugins/colors.ts b/src/runtime/vue/plugins/colors.ts new file mode 100644 index 0000000000..e0a05c34b9 --- /dev/null +++ b/src/runtime/vue/plugins/colors.ts @@ -0,0 +1,35 @@ +import { computed, watchEffect } from 'vue' +import { useHead } from '@unhead/vue' +import type { Plugin } from 'vue' +import { useAppConfig } from '../composables/useAppConfig' +import { generateColorStyles } from '../../utils/colors' + +export default { + install(app) { + app.runWithContext(() => { + const appConfig = useAppConfig() + + const root = computed(() => generateColorStyles(appConfig.ui.colors)) + + useHead({ + style: [{ + innerHTML: root, + tagPriority: -2, + id: 'nuxt-ui-colors' + }] + }) + + if (typeof document !== 'undefined') { + watchEffect(() => { + let styleEl = document.querySelector('#nuxt-ui-colors-vue') as HTMLStyleElement + if (!styleEl) { + styleEl = document.createElement('style') + styleEl.id = 'nuxt-ui-colors-vue' + document.head.appendChild(styleEl) + } + styleEl.innerHTML = root.value + }) + } + }) + } +} satisfies Plugin