Dominik Traskowski
Dominik Traskowski

Reputation: 1179

Vue.js dynamic <style> with variables

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>

Upvotes: 98

Views: 220640

Answers (16)

dlc
dlc

Reputation: 25

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"

Upvotes: 0

Namysh
Namysh

Reputation: 4617

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

Upvotes: 95

mythz
mythz

Reputation: 143284

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 }
  }
}

Upvotes: 0

Jon Freynik
Jon Freynik

Reputation: 369

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>

Upvotes: 0

Miguel Q
Miguel Q

Reputation: 3618

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>

Upvotes: 0

Daniel Danielecki
Daniel Danielecki

Reputation: 10502

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.

Upvotes: 0

MikhailM
MikhailM

Reputation: 71

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.

Upvotes: 1

Fletcher Rippon
Fletcher Rippon

Reputation: 1975

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

Upvotes: 6

Johnson Fashanu
Johnson Fashanu

Reputation: 1130

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>

Upvotes: 5

Laurent Meyer
Laurent Meyer

Reputation: 2881

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
   }
}

Upvotes: 2

Lohith
Lohith

Reputation: 934

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>

Upvotes: 0

Sebastian
Sebastian

Reputation: 1946

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.

Upvotes: 9

Didarul Alam Rahat
Didarul Alam Rahat

Reputation: 851

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}">

Upvotes: 69

Muthu Kumaran
Muthu Kumaran

Reputation: 17900

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>

Upvotes: 18

Niklesh Raut
Niklesh Raut

Reputation: 34914

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>

Upvotes: 10

Mickey Mullin
Mickey Mullin

Reputation: 599

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.

Upvotes: 42

Related Questions