101

Is it possible to add the dynamic variable in style?

I mean something like:

<style>
    .class_name {
        background-image({{project.background}});
    }
    @media all and (-webkit-min-device-pixel-ratio : 1.5),
    all and (-o-min-device-pixel-ratio: 3/2),
    all and (min--moz-device-pixel-ratio: 1.5),
    all and (min-device-pixel-ratio: 1.5) {
        .class_name {
            background-image({{project.background_retina}});
        }
    }
</style>
2
  • CSS is static. If your style is really complex, maybe you will need some CSS pre-processor. Commented Nov 16, 2017 at 7:27
  • an article on css variable and sass variable calculation function calc() Commented May 9, 2023 at 9:16

16 Answers 16

97

With Vue.js 3.2 you can do State-Driven Dynamic CSS like this:

<template>
    <h1 id="script">Script</h1>
    <h1 id="scriptSetup">Script setup</h1>
</template>

<script>
  export default {
    data() {
      return {
        colorFromScript: 'red'
      }
    }
  }
</script>

<script setup>
const colorFromScriptSetup = 'green'
</script>

<style>
  #script {
      color: v-bind('colorFromScript')
  }

  #scriptSetup {
      color: v-bind('colorFromScriptSetup')
  }
</style>

See an implementation here

v-bind in CSS Docs

Sign up to request clarification or add additional context in comments.

3 Comments

This is the best solution for those using Vue 3.
this doesn't work with nested scss selectors
Also, work for Vue 2.7
69

I faced the same problem. I have been trying to use a background color value from a database. I find out a good solution to add a background color value on inline CSS which value I set from database.

<img :src="/Imagesource.jpg" alt="" :style="{'background-color':Your_Variable_Name}">

6 Comments

Works well without the { and } in the :style
this does not work if you use lib. I need apply to apply style to the child of child component.
you can also write background-color in snake-case, if you don't want to enclose it around extra pair of inverted commas. It looks neat and saves time :style="{backgroundColor:Your_Variable_Name}"
@MuhammadNaufil not snake_case but camelCase, other than that good advice :)
How would you do this with something like transform: rotateZ(0);?
|
42

The best way to include dynamic styles is to use CSS variables. To avoid inline styles while gaining the benefit (or necessity—e.g., user-defined colors within a data payload) of dynamic styling, use a <style> tag inside of the <template> (so that values can be inserted by Vue). Use a :root pseudo-class to contain the variables so that they are accessible across the CSS scope of the application.

Note that some CSS values, like url() cannot be interpolated, so they need to be complete variables.

Example (Nuxt .vue with ES6/ES2015 syntax):

<template>

<div>
  <style>
    :root {
      --accent-color: {{ accentColor }};
      --hero-image: url('{{ heroImage }}');
    }
  </style>
  <div class="punchy">
    <h1>Pow.</h1>
  </div>
</div>

</template>
<script>

export default {
  data() { return {
    accentColor: '#f00',
    heroImage: 'https://vuejs.org/images/logo.png',
  }},
}

</script>
<style>

.punchy {
  background-image: var(--hero-image);
  border: 4px solid var(--accent-color);
  display: inline-block;
  width: 250px; height: 250px;
}
h1 {
  color: var(--accent-color);
}

</style>

Also created an alternate more involved runnable example on Codepen.

4 Comments

Tried your solution in the component with <Templates> and get this error: Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>, as they will not be parsed.
This is the right idea, but as Nelly points out, Vue throws a warning or may even not load depending on version in use (minified Vue appears to allow this). See Muthu's answer (specifically their Update section) or this SO answer for a better solution
instead of putting the CSS variables in a <style> block, you can build an object with each variable name as the property along with its value as a computed prop or method. Then simply bind this object to the root element's style attribute.
Unfortunately HTML validators will throw an error for <style> tags placed within a <div> tag.
18

CSS <style> is static. I don't think you can do that... you might have to look for a different approach.

You can try using CSS variables. For example, (the code below is not tested)

<template>
    <div class="class_name" :style="{'--bkgImage': 'url(' + project.background + ')', '--bkgImageMobile': 'url(' + project.backgroundRetina + ')'}">
    </div>
</template>

<style>
    .class_name{
        background-image: var(--bkgImage);
    }
    @media all and (-webkit-min-device-pixel-ratio : 1.5),
        all and (-o-min-device-pixel-ratio: 3/2),
        all and (min--moz-device-pixel-ratio: 1.5),
        all and (min-device-pixel-ratio: 1.5) {
            .class_name {
                background-image: var(--bkgImageMobile);
            }
        }
</style>

Note: Only the latest browsers support CSS variables.

If you still see any issues with the :style in the template then try this,

<div :style="'--bkgImage: url(' + project.background + '); --bkgImageMobile: url(' + project.backgroundRetina + ')'">
</div>

Comments

10

As you are using Vue.js, use Vue.js to change the background, instead of CSS:

var vm = new Vue({
    el: '#vue-instance',
    data: {
        rows: [
            {value: 'green'},
            {value: 'red'},
            {value: 'blue'},
        ],
        item:""
    },
    methods:{
        onTimeSlotClick: function(item){
            console.log(item);
            document.querySelector(".dynamic").style.background = item;
        }
    }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
<div id="vue-instance">
    <select class="form-control" v-model="item" v-on:change="onTimeSlotClick(item)">
        <option value="">Select</option>
        <option v-for="row in rows">
            {{row.value}}
        </option>
    </select>
    <div class='dynamic'>VALUE</div>
    <br/><br/>
    <div :style="{ background: item}">Another</div>
</div>

3 Comments

Great but in an inline method, i cannot add a background for retina. I know that method.
You can add class, toggle, v-show, v-if to achieve this
I can do it with two div and class to show one div depends of retina or not. But.. It will load two images always. v-if or v-show sound good but how to check if we are on a retina display with v-if? Or how to check it in JS.. Maybe i will found solution in that way.
9

Yes, this is possible. Vue.js does not support style tags in templates, but you can get around this by using a component tag. Untested pseudocode:

In your template:

<component type="style" v-html="style"></component>

In your script:

props: {
    color: String
}
computed: {
    style() {
        return `.myJSGeneratedStyle { color: ${this.color} }`;
    }
}

There are lots of reasons why you shouldn't use this method. It's definitely hacky and :style="" is probably better most of the time, but for your problem with media queries I think this is a good solution.

1 Comment

With Vue 2.6.11 I could not get the <component> to work so this answer may not work.
6

Vue 3 State-Driven Dynamic CSS Variables

I know this is a bit late and is using Vue.js 2, but as of now in Vue.js 3 you can create state-driven CSS variables.

You can now use your SFC (Single File Component) state data inside your styles tags using v-bind().

You can read more about state-driven CSS variables here, or read the Vue.js 3 documentation here.

Here is a code example

Example

<template>
  <div>
    <input type="text" v-model="color" />
    <div class="user-input-color">
      {{ color }}
    </div>
  </div>
</template>

<script>
export default {
  data: () => ({
    color: 'white'
  })
}
</script>

<style scoped>
.user-input-color {
  background-color: v-bind(color)
}
</style>

Here is a link to the live example.

Links

1 Comment

You need quotes to include the v-bind variable like v-bind('color').
5

You can use the component tag offered by Vue.js.

<template>
  <component :is="`style`">
    .cg {color: {{color}};}
  </component>
  <p class="cg">I am green</p> <br/>
  <button @click="change">change</button>
</template>
<script>
  export default {
    data(){
      return { color: 'green' }
    },
    methods: {
      change() {this.color = 'red';}
    }
  }
</script>

1 Comment

Even if this could work, this Sz.'s answer here suggests that it might not be a good practice: stackoverflow.com/a/50154225/14008447
2

I encountered the same problem and I figured out a hack which suits my needs (and maybe yours).

As <style> is contained in <head>, there is a way to make it work:

We generate the CSS content as a computed property based on the state of the page/component

computed: {
    css() {
        return `<style type="text/css">
         .bg {
            background: ${this.bg_color_string};
         }</style>`
    }
}

Now, we have our style as a string and the only challenge is to pass it to the browser.

I added this to my <head>

<style id="customStyle"></style>

Then I call the setInterval once the page is loaded.

 mounted() {
     setInterval(() => this.refreshHead(), 1000);
 }

And I define the refreshHead as such:

methods: {
   refreshHead() {
       document.getElementById('customStyle').innerHTML = this.css
   }
}

Comments

1

I needed to write completely dynamic styles, so I used approach beyond Vue system:

{
   // Other properties.
    watch: {
        myProp: {
            handler() {
                this.styleElement.innerHTML = this.myProp.css;
            },
            deep: true,
        },
    },
    mounted() {
        this.styleElement = this.document.createElement('style');
        this.styleElement.innerText = this.myProp.css;
        this.document.head.append(this.styleElement);
    },
    unmounted() {
        this.styleElement.remove();
    },
}

Though it may have some performace issues with CSS big enough.

Comments

0

In simple terms, this is how you would do it in Vue.js and Nuxt.js:

<template>
    <div>
        <img :src="dynamicImageURL" alt="" :style="'background-color':backgroundColor"/>
    </div>
</template>

<script>
    export default{
        data(){
           return {
                 dynamicImageURL='myimage.png',
                 backgroundColor='red',
            }
        }
    }
</script>

1 Comment

Doesn't work. Says "parsing error".
0

I liked @mickey-mullin reply, but not everything worked entirely. The url missed require, even though the information in his post helped me a lot in my case.

var(), url(), multiple ternary operators (my own case - you shouldn't need it), I was able to do so for background-image in such a way:

template

<div :style="[
  case1 ? { '--iconUrl': `url(${require('../../../public/icon1.svg')})`} :
  case2 ? { '--iconUrl': `url(${require('../../../public/icon2.svg')})`} :
  { '--iconUrl': `url(${require('../../../public/default.svg')})` },
]" class="myClass">

styles

div.myClass::before {
  background-image: var(--iconUrl);
}

Note: I didn't have to declare iconUrl in my data() -> return.

Comments

0

This is simple to do, even better if already using a framework / css variables.

By adding style to your html that would overwrite the root/variables according to loaded "dynamic css"

In that case using css vars in your css

<style>
.myTheme{
  color:var(--theme-color)
}
</style>

then having a computed property like:

computed:{
 accountTheme(){
  return {"--theme-color":this.themeColor}
 }
}

Then whatever method that would change such dynamic theme you do it

methods:{
 load(){
  ....
  this.themeColor="red"
 }
}

Apply that dynamic style to html element

<html :style="accountTheme"></html>

Comments

0

This works for me - if you add the style tags along with the definitions and it should work.

<template>
    <div class="wrap">
        <div v-html="customStyle"></div>
    </div>
</template>

<script setup>
import { ref } from "vue";
const customStyle = ref("<style>body{background:blue!important;}</style>");
</script>

Comments

0

I was able to embed multiple CSS rules when using progressive Vue.js (i.e. no npm / build step) using Vue's dynamic <component>:

const MyComponent = {
  template:`<div>
    <component is="style" v-html="css"></component>
  </div>`,
  setup() {
     const css = `
        .animation-pulse {
            animation: pulse 2s infinite;
        }
        @keyframes pulse {
            0% {
                transform: scale(0.8);
                box-shadow: 0 0 0 0 rgba(229, 62, 62, 1);
            }
            70% {
                transform: scale(1);
                box-shadow: 0 0 0 60px rgba(229, 62, 62, 0);
            }
            100% {
                transform: scale(0.8);
            }
        }`
     return { css }
  }
}

Comments

0

In Vue 3, you can do conditions in your css via v-bind too. Like

<script>

 data() {
   return {
     hasColor: true
   };
 }
    
</script>

<style scoped lang="scss">
    
.class-name {
  background-color: v-bind('!hasColor ? "blue" : "red"');
}
    
</style>

Tested it with "vue": "^3.2.37"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.