Binding CSS Variables in Vue
🗓 February 9, 2020
Background
Binding to style
and class
in Vue.js can be extremely handy for making styles respond to state in your application. This state might be data fetched through an Ajax request, user input, or any other type of data source that is made available at run time. Binding a CSS Variable to style
is a clever way to push this pattern a little bit further, while gaining all the benefits of writing variable driven css.
Simple Style Binding Example
Let's start with a simple (non-css-variable) example to illustrate. Let's say we have an E-book app. To give the user the best reading experience, you allow them to select which font they prefer to use. You might handle this case like so.
<template>
<div :style="userStyle">
E-Book Text here
</div>
</template>
<script>
export default {
data () {
return {
fontOptions: ['Roboto', 'Lobster', 'Comic Sans'],
userSelectedFont: 'Roboto'
}
},
computed: {
userStyle () {
return {
fontFamily: this.userSelectedFont
}
}
}
</script>
Notice how we capture the user select font in our component state and bind that to the style
attribute of the element that renders our text. This is classic Vue style binding and works great!
Now with a CSS Variable
Let's tackle the same use case, but using the CSS variable pattern...
<template>
<div
:style="userStyle"
class="book-text"
>
E-Book Text here
</div>
</template>
<script>
export default {
data () {
return {
fontOptions: ['Roboto', 'Lobster', 'Comic Sans'],
userSelectedFont: 'Roboto'
}
},
computed: {
userStyle () {
return {
'--user-font-fam': this.userSelectedFont
}
}
}
</script>
<style scoped>
.book-text {
font-family: var(--user-font-fam);
}
</style>
The difference here is subtle but important. Instead of binding our font choice directly to the font-family
style in the style
attribute, we bind the choice to a css variable, called --user-font-fam
. And --user-font-fam
can now be referenced by CSS selectors on elements under our div
.
When to Reach For CSS Variable Binding
Most of the time it's not a problem to just bind directly to the style attribute with whatever styling you want, and you don't need to use a CSS variable... but there a few scenarios where the CSS variable can be quite helpful.
1) Reuse
If you find yourself binding the same piece of state to many different style
attributes, you might be better served by creating the CSS variable. This will let you author CSS in your normal pattern, but this CSS will benefit from Vue's renowned reactivity system!
2) Psuedo Elements
Sometimes we want to styling things without a style
attribute to bind to (like psuedo elements). In these cases, having our CSS variable handy works wonders...
p::first-line {
color: var(--theme-secondary-color);
}
3) Math
If you're using math to calculate layout it can be nice to stash your math in a css variable... especially of you want CSS to do some of the work. Along these lines:
<template>
<main :style="bannerStyle">
<div class="banner">
Some Info Banner
<div class="banner-conent">
banner content
</div>
</div>
</main>
</template>
<script>
export default {
computed: {
bannerWidth () {
const fooWidth = document.getElementById('foo').offsetWidth
return `${fooWidth / 2}px`
},
bannerStyle () {
return {
'--banner-width': this.bannerWidth
}
}
}
</script>
<style scoped>
.banner {
width: var(--banner-width);
}
.banner-content {
width: calc(var(--banner-width) - 20px);
}
</style>