Subscribe on changes!

Allow global scope on v-bind in <style>

avatar
Nov 27th 2021

What problem does this feature solve?

Allows scoping css and css variables to elements outside vue app (such as body).

<script>
const bgcolor = 'red';
</script>

<template>
<!-- works -->
<component :is="'style'" type="text/css">
body { 
    --some-var: {{bgcolor}};
    background-color: var(--some-var)  !important; 
}
</component>
</template>

<style>
/* does not work */
body { 
    --some-var: v-bind(bgcolor);
    background-color: var(--some-var) !important;     
}
</style>

What does the proposed API look like?

<style global>
avatar
Nov 28th 2021

What problem does that solve? How is this better than simply assigning a custom property to document.body.style?

Is the scoping of the variable name solving any naming collision issues you would face with a non-scoped global name? Or wouldn't that rather be a hindrance as we are dealing with global custom properties anyhow, which should likely be changeable from more than one place?

How should the custom property behave when the component is unmounted? Should we remove it? So far there is no lifecycle to styles specifically.

avatar
Nov 29th 2021

I agree that my problem can be solved in a different way, but it would be a nice-to-have feature to be able to do --my-var: v-bind(color) where --my-var is scoped to body, but considering v-bind generated css vars are scoped to component via inline style I suspect this is not possible with how things work today. I'll move forward with using <component :is="'style'" instead.

avatar
Jun 18th 2023

@LinusBorg this looks like a bug. As I understand it, the entire purpose of <style scoped> is to prevent styles from being applied globally... so shouldn't @olemarius 's request be the default behavior?

avatar
Jul 27th 2023

I suggest we reopen this issue. Vue happily compiles unscoped <style lang="scss">…</style> containing v-bind(my-variable) and it's completely non-obvious that my-variable will not be available when the styles affect HTML elements outside the component's DOM subtree. If v-bind variables are used in unscoped styles, I would expect Vue to set the corresponding CSS variables on the <html> or <body> tag instead of on the component's root. But that's obviously just my opinion – one could certainly think of other solutions (a new <style global> section being one of them).

In any case, the compiler should at least complain that variables might not be available in unscoped styles.

What problem does that solve? How is this better than simply assigning a custom property to document.body.style?

The problem this solves is that I won't have to worry about component mounting/unmounting/reactivity etc. as Vue takes care of that for me. I was manipulating the DOM directly at first, but then noticed that v-bind would reduce the amount of code quite a bit, only to realize that it doesn't work. :(

avatar
Aug 1st 2023

Came across the same issue today. Was wondering why v-bind styles weren't applied globally. In my case I have theme colors that change on a page component, and that I want modals and popups to respect, however since they are rendered outside of the page component even though I use :global the theme variables apply but with undefined values it seems.

avatar
Aug 1st 2023

This is not a bug.

The actual value will be compiled into a hashed CSS custom property, so the CSS is still static. The custom property will be applied to the component's root element via inline styles and reactively updated if the source value changes.

https://vuejs.org/api/sfc-css-features.html#v-bind-in-css

  • There is one CSS custom property name generated at compile time
  • This custom property is added to the component's root element

If we add this property to body, then multiple instances of a component would override each other's custom property, which can then lead to race conditions depending on the order of component mounts and updates.

What you want/would need is something that works very different - you want essentially a dynamic CSS property name, generated at runtime, per component instance, that is dynamically added to and removed from body during mount/unmount.

This is a feature request that could be proposed and discussed, but it's not a bug in the current implementation, which is explicitly focussed on elements within the component.

However, what we do provide is support with <teleport>. if you use <teleport> (usually used with modals etc) in a component, then we do re-apply the custom properties to the wrapper element that you teleported to.

avatar
Aug 1st 2023

@LinusBorg No, this is indeed not a bug, as you say. But what @olemarius posted was a feature request, so this seems very much in line with what you're saying? :)

(I agree with your assessment of the race condition risk, though. In my case, I have a single top-level "layout" component which interacts with the outside DOM, so for me personally that'd be a risk I'd be willing to take. But I definitely understand your concerns.)