MingWen
MingWen

Reputation: 1353

In vue.js component, how to use props in css?

I'm new to vue.js. Here is my problem:

In a *.vue file like this:

<template>
  <div id="a">
  </div>
</template>

<script>
  export default {
    name: 'SquareButton',
    props: ['color']
  }
</script>

<style scoped>
    #a {
      background-color: ?
    }
<style>

How can I use the props color in background-color: (where is a ? now).

Thanks.

Upvotes: 134

Views: 130372

Answers (10)

GuiMendel
GuiMendel

Reputation: 719

I know we're talking vue 2 here, but in case anyone from vue 3 lands in this question (like I did), vue 3 introduced a much cleaner way to do this:

<template>
  <div id="a">
  </div>
</template>

<script>
  export default {
    name: 'SquareButton',
    props: ['color']
  }
</script>

<style scoped>
    #a {
      background-color: v-bind(color);
    }
<style>

What Vue actually does behind the scenes is the same "introducing css variables through component's style process", but it sure looks much better on the eyes now.

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

Upvotes: 57

dotNET
dotNET

Reputation: 35380

As a note for myself and future readers, if you're using Vue 3's Composition API with <script setup lang="ts">, the following syntax works correctly:

In the script section, define your prop(s):

const props = defineProps<{
  myprop: string;
}>()

In the styles section:

.some-class {
  background-image: v-bind('props.myprop');
}

Note that the prop name is enclosed in quotes.

Upvotes: 2

user3504541
user3504541

Reputation: 77

A simple component to add css styles in Vue 2, using <style> tag

<template>
    <span class="embed-style" style="display: none" v-html="cssText"></span>
</template>

<script>
export default {
    name: 'EmbedStyle',
    props: {
        rules: {
            type: Array,
            default() {
                return []
            }
        }
    },
    computed: {
        cssText() {
            let txt = ' ';
            this.rules.forEach(rule => {
                txt += `${rule.selector} { `;
                for (const prop in rule.props) {
                    txt += `${this.fromCamelCase(prop)}: ${rule.props[prop]}; `;
                }
                txt += ' } ';
            })
            return `<style>${txt}</style>`;
        }
    },
    methods: {
        fromCamelCase(str) {
            let newStr = '';
            for (let l = 0; l < str.length; l++) {
                if (/[A-Z]/.test(str.charAt(l))) {
                    const lower = str.charAt(l).toLowerCase();
                    newStr += `-${lower}`;
                } else {
                    newStr += str.charAt(l);
                }
            }
            return newStr;
        }
    }
}
</script>

Then we use this way:

<template>
  <embed-style :rules="[cssRules]" />
...
    
computed: {
        cssRules() {
            return {
                selector: '.toaster .toast',
                props: {
                    animationDuration: `${this.duration}ms !important`,
                    transitionDuration: `${this.duration}ms !important`
                }
            }
        }
}

Upvotes: 0

ckhatton
ckhatton

Reputation: 1572

You could utilise the CSS var(--foo-bar) function. It is also useful if you are trying to pass an asset that has its own dynamic path, like Shopify does.

This method also works for styling the :before and :after elements as they refer back to the style applied on the owner element.

Using the original post example for passing a colour:

<template>
  <div
    id="a"
    :style="{ '--colour': color }">
  </div>
</template>

<script>
  export default {
    name: 'SquareButton',
    props: ['color']
  }
</script>

<style scoped>
  #a {
    background-color: var(--colour);
  }
</style>

Using the original post example for passing an URL:

<template>
  <div
    id="a"
    :style="{ '--image-url': 'url(' + image + ')' }">
  </div>
</template>

<script>
  export default {
    name: 'SquareButton',
    props: ['image']
  }
</script>

<style scoped>
  #a {
    background-url: var(--image-url);
  }
</style>

Source

Upvotes: 5

Freestyle09
Freestyle09

Reputation: 5508

Vue 3 added new way of binding styles, so now you can easily bind your props to css properties.

Read source: https://learnvue.co/2021/05/how-to-use-vue-css-variables-reactive-styles-rfc/

<template>
  <div>
    <div class="text">hello</div>
  </div>
</template>

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

<style>
  .text {
    color: v-bind(color);
  }
</style>

Upvotes: 10

Emad Ahmed
Emad Ahmed

Reputation: 570

As we are in 2020 now, I suggest using this trick with a css function called var

<template>
    <div id="a" :style="cssVars"></div>
</template>

<script>
export default {
    props: ['color'],
    computed: {
      cssVars () {
        return{
          /* variables you want to pass to css */
          '--color': this.color,
        }
    }
}
<script>

<style scoped>
#a{
    background-color: var(--color);
}
</style>

This method is very useful because it allows you to update the passed values through css later on (for example when you apply hover event).

credit

Upvotes: 47

Yuri M
Yuri M

Reputation: 1371

You actually can!

You should define the CSS variables in a Computed Property, then call the computed property as a style attribute to the element that will require the CSS variable, and finally you may use the variable within the tags at the bottom of your document.

new Vue({
  el: '#app',
  data: function() {
    return {
      baseFontSize: 1,
      bgHoverColor: "#00cc00",
      hoverContent: "Hovering!"
    }
  },
  computed: {
    cssProps() {
      return {
        '--hover-font-size': (this.baseFontSize * 2) + "em",
        '--bg-hover-color': this.bgHoverColor,
        '--hover-content': JSON.stringify(this.hoverContent)
      }
    }
  }
})
div {
  margin: 1em;
}

div.test:hover {
  background-color: var(--bg-hover-color);
  font-size: var(--hover-font-size);
}

div.test:hover::after {
  margin-left: 1em;
  content: var(--hover-content);
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>

<div id="app" :style="cssProps">

  <div>Hover text: <input type="text" v-model="hoverContent"></div>
  <div>Hover color: <input type="color" v-model="bgHoverColor"></div>

  <div class="test">Hover over me</div>
</div>

Or have a look here: https://codepen.io/richardtallent/pen/yvpERW/
And here: https://github.com/vuejs/vue/issues/7346

Upvotes: 131

phen0menon
phen0menon

Reputation: 2452

Why not just use :style prop in this way:

<template>
  <div :style="{ backgroundColor: color }">
</template>

<script>
export default {
  props: {
    color: {
      type: String,
      default: ''
    }
  }
}
</script>

Make sure you define css properties in camelCase style.

Upvotes: 24

gwildu
gwildu

Reputation: 3921

If you need css that can't be applied by a style attribute like pseudo classes or media queries, what I do is the following:

Create a globally available style component when initializing Vue (you need it as otherwise you run into linting issues). It creates a style tag that simply renders the content in the slot:

I would only use this if you really need both dynamic values in your css and css features that can't be applied to a style attribute.

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

Vue.config.productionTip = false
Vue.component('v-style', {
  render: function(createElement) {
    return createElement('style', this.$slots.default)
  }
})

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

Then use it at the top of your template like this and you get the full JavaScript scope of your component and the full css syntax combined:

<template>
  <v-style>
    @media screen and (max-width: 820px) {
      .gwi-text-media-{{ this.id }} {
        background-image: url({{ mobileThumb }});
      }
    }
  </v-style>
</template>

It seems a bit hacky to me, but it does it's job and I would rather go like this in some cases than having to add additional JS for mouse-over or resize events that have a big potential to slow down your application performance.

Upvotes: 17

Potray
Potray

Reputation: 1998

You don't. You use a computed property and there you use the prop to return the style of the div, like this:

<template>
  <div id="a" :style="style" @mouseover="mouseOver()">
  </div>
</template>

<script>
  export default {
    name: 'SquareButton',
    props: ['color'],
    computed: {
      style () {
        return 'background-color: ' + this.hovering ? this.color: 'red';
      }
    },
    data () {
      return {
        hovering: false
      }
    },
    methods: {
      mouseOver () {
       this.hovering = !this.hovering
      }
    }
  }
</script>

<style scoped>
<style>

Upvotes: 86

Related Questions