diff --git a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts index acba3315862..1fb4cc65fd0 100644 --- a/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts +++ b/packages/runtime-dom/__tests__/helpers/useCssVars.spec.ts @@ -385,6 +385,44 @@ describe('useCssVars', () => { } }) + test('with delay mount child', async () => { + const state = reactive({ color: 'red' }) + const value = ref(false) + const root = document.createElement('div') + + const Child = { + setup() { + onMounted(() => { + const childEl = root.children[0] + expect(getComputedStyle(childEl!).getPropertyValue(`--color`)).toBe( + `red`, + ) + }) + return () => h('div', { id: 'childId' }) + }, + } + const App = { + setup() { + useCssVars(() => state) + return () => (value.value ? h(Child) : [h('span')]) + }, + } + + render(h(App), root) + await nextTick() + // css vars use with fallback tree + for (const c of [].slice.call(root.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe(`red`) + } + + // mount child + value.value = true + await nextTick() + for (const c of [].slice.call(root.children as any)) { + expect((c as HTMLElement).style.getPropertyValue(`--color`)).toBe(`red`) + } + }) + // #8826 test('with custom element', async () => { const state = reactive({ color: 'red' }) diff --git a/packages/runtime-dom/src/helpers/useCssVars.ts b/packages/runtime-dom/src/helpers/useCssVars.ts index 6331208c5c0..e2bc6de9278 100644 --- a/packages/runtime-dom/src/helpers/useCssVars.ts +++ b/packages/runtime-dom/src/helpers/useCssVars.ts @@ -3,8 +3,10 @@ import { Static, type VNode, getCurrentInstance, + onBeforeUpdate, onMounted, onUnmounted, + queuePostFlushCb, warn, watch, } from '@vue/runtime-core' @@ -47,6 +49,12 @@ export function useCssVars(getter: (ctx: any) => Record): void { updateTeleports(vars) } + // handle cases where child component root is affected + // and triggers reflow in onMounted + onBeforeUpdate(() => { + queuePostFlushCb(setVars) + }) + onMounted(() => { // run setVars synchronously here, but run as post-effect on changes watch(setVars, NOOP, { flush: 'post' })