Skip to content

fix(vue): make theme reactive #3971

New issue

Have a question about this project? No Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “No Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? No Sign in to your account

Open
wants to merge 6 commits into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/plugins/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 }))
}
Expand Down
34 changes: 2 additions & 32 deletions src/runtime/plugins/colors.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down
34 changes: 34 additions & 0 deletions src/runtime/utils/colors.ts
Original file line number Diff line number Diff line change
@@ -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<string, string>) {
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 ')}
}
}`
}
5 changes: 4 additions & 1 deletion src/runtime/vue/composables/useAppConfig.ts
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions src/runtime/vue/plugins/colors.ts
Original file line number Diff line number Diff line change
@@ -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
Loading