VueL_870x220

In this article, I will share with you the process of discovering one of the not-so-common features of Vue.js, the injection of CSS variables from the component's script. Let's get a headache together and learn some Vue!

So. A few days ago, I was faced with an interesting challenge at work. The need to pass down some CSS style attributes into my <style> tags for controlling a couple of different properties with the same value, as well as performing some calc results.

After digging around Google and Github issues, I found a couple solutions that gave me a deeper understanding of Vue, and was definitely a fun little challenge that I want to share with you. πŸ˜‹

Initial Reaction

For the sake of example, let’s pretend you have a button component that you want to pass some properties that will control its height and the background color.

In reality, what I was trying to do was a lot more complex since it involved a bunch of SCSS calculations and CSS animations, but I like keeping my articles clutter free - so bear with me.

<template>
  <button :style="btnStyles">My button</button>
</template>

<script>
export default {
  name: 'SimpleButton',
  props: {
    bgColor: {
      type: String,
      default: "#0099CC"
    },
    height: {
      type: Number,
      default: 100
    }
  },
  computed: {
    btnStyles() {
      return {
        "background-color": this.bgColor,
        height: `${this.height}px`
      };
    }
  }
};
</script>

Alright, so this would be a SimpleButton class, super useless, and as UGLY as I managed to make it. Just kidding, I’m actually a UX designer undercover. πŸ‘©‍πŸ’»

Coming back to the simplicity of the example, however, what I’m trying to do here is make this button’s CSS controllable by properties. I’m fully aware that this example is flawed, and that I could just simply be binding attributes into classes - but as stated, I don’t want to overcomplicate things.

It does, on the other hand, illustrate my first approach to the problem - put everything in an inline style tag. SURE, ok this works just fine, but it starts to become really verbose and complicated to follow once you have N levels of nested divs each, with their own different tags inside that need to be dynamic. Even more so if you’re trying to get SCSS to work in your favor with some calculations, mixins, functions, etc.

So at this point, I got my stubborn hat on and decided I had to find a solution that would allow me to pass in these variables into the <style>.

Being Stubborn

Vue 1.0 had the ability to somewhat interpolate variables into the <style> tag, and would allow you to set up CSS variables like so: --bg-color: {{ bgColor }};

However this came with a huge cost, because on every re-render Vue was having to do crazy things like recompiling these styles. Needless to say, it doesn’t work like this today. <style> tags are static, and once the component is mounted, that’s that.

At this point, stubborn was the name of the game. So I got digging and found a couple posts that pointed me in a similar direction. Create CSS variables on the inline style and then use them in the SCSS. Yeah…

So how do we do this?

Let’s start by first switching some things around on our previous example.

Go to the computed props, and replace the btnStyles with this new cssVars property.

computed: {
    cssVars() {
      return {
        '--bg-color': this.bgColor,
        '--height': this.height + 'px'
      }
    }
  }

Alright, so now that we are dynamically generating these variables - how do we use them? Simple, let’s pass them to the <button> through the :style binding like we were doing before.

<button :style="cssVars">My button</button>

Yaaay…? Don’t worry, SOMETHING happened. Go to your developer tools and inspect the button, you’ll see in the element.style tab:

element-style

Go ahead and add these prop-created CSS variables into your style block for the button component.

<style scoped>
button {
  color: var(--text-color);
  background-color: var(--bg-color);
  height: var(--height);
}
</style>

Go ahead and run this in your browser, tada ~!

Testing Reactivity

Alright so the theory is really nice, but how do we actually test the reactivity of the CSS variables?

There are two ways to do it with what we currently have: mount the button component into your app, and pass in different props.

Or, for the sake of simplicity, we’re going to add a local data state so we can modify it on Vue developer tools.

Add this to your button logic.

data() {
    return {
      textColor: "blue"
    };
  },

Finally, don’t forget to add the color property to your styles.

<style scoped>
button {
  color: var(--text-color);
  background-color: var(--bg-color);
  height: var(--height);
}
</style>

Alright, now that we’re all set up - go to your browser and open the app. You’ll see that your button’s text is blue, as expected, since that is what we first set up as textColor on our state.

Now go into your Vue dev tools, and click on the property and change it to red or your color of choice. You’ll immediately see that the color of the button’s text changes as well.

Wait, what? πŸ€”

At this point you’re either fascinated by the endless possibilities of this approach, (well maybe not endless - it’s quite a niche solution to a very small handful of problems), or you are completely and utterly confused.

If you’re one of those in the second group, and you’re thinking that this whole mess could have been solved by :style or :class bindings, you’re absolutely right. Congratulations! However, as I mentioned, there are very specific SCSS scenarios where this starts becoming handy.

Example? How about calculating a menu’s position based on a the size of the menu and the internal icon’s size?

'--menu-position': ( ( ( this.menuSize * 2) - this.iconSize ) / -2 ) + 'px',

Another example could be using them to do some CSS math. (🀒)

height: calc(var(--height) * 10);

Conclusion

Frankly, this whole endeavor was fun to investigate and apply to an actual project, and I’m sure I’ll run into a moment in my life where I’ll be glad I have this handy tool under my belt. So on that note, I hope at the very least you were entertained πŸ˜….

For More Info on Building Great Web Apps

Want to learn more about creating great web apps? It all starts out with Kendo UI - the complete UI component library that allows you to quickly build high-quality, responsive apps. It includes everything you need, from grids and charts to dropdowns and gauges.

Learn More about Kendo UI

Get a Free Trial of Kendo UI



Marina Mosti
About the Author

Marina Mosti

Marina Mosti is a full-stack web developer with over 13 years of experience in the field. She enjoys mentoring other women on JavaScript and her favorite framework, Vue, as well as writing articles and tutorials for the community.

She currently holds a position as Lead FE Developer at VoiceThread, and she is the author of the FormVueLatte library as well as a member of the Vuelidate team. In her spare time, she enjoys playing bass, drums, and videogames.

Related Posts

Comments

Comments are disabled in preview mode.