CSS variables use in Vue

Viewed 40277

Is it possible to use pure CSS variables with Vue without having to link any stylesheets or use SASS/PostCSS? Unsure why I'm unable to get this to work in its most basic form.

<template>
   <div id="test">
        TEST
    </div>
</template> 
<style scoped> 
   :root {
   --var-txt-color: #c1d32f;
   }

 #test {
 color: var(--var-txt-color);
  }
</style> 
6 Answers

I know you highlighted "without having to link any stylesheet", but I run into the same issue and there is a simple option - use just one external css file and include it in your App.vue, then you can access the variables anywhere else, in scoped styles as well.

variables.css

:root {
  --font-family: "Roboto", "Helvetica", "Arial", sans-serif;
  --primary-color: #333a4b;
}

App.vue

<style>
  @import './assets/styles/variables.css';
</style>

LandingView.vue

<style scoped>
  #landing-view {
    font-family: var(--font-family);
    font-weight: 300;
    line-height: 1.5em;
    color: var(--primary-color);
  }
</style>

This won't work as expected because of scoped attribute for stylesheet. Example above compiles into:

[data-v-4cc5a608]:root {
  --var-txt-color: #f00;
}

And, as you understand, it will not target actual :root element.

It can be solved by:

  • Not using scoped attribute for this stylesheet. Notice that it may cause styles conflict with other variables declarations for :root element.

  • Using current component's wrapping element as root. If we declare variables this way:

    .test {
      --var-txt-color: #c1d32f;
      color: var(--var-txt-color);
    }
    
    .test-child-node {
      background-color: var(--var-txt-color);
    }
    

Then it will can reuse variables for other elements of the same component. But still, it won't be possible to use declared variables inside child components without removing scoped, if it is the case.

Why not just use this?

<style scoped>
  * {
    --var-txt-color: #c1d32f;
  }
</style>

The generated CSS is:

*[data-v-d235d782] {
  --var-txt-color: #c1d32f;
}

This has been working for me.

I just discovered that it looks like this also works, using the "deep selector"

>>>  {
  --var-txt-color: #c1d32f;
}

Generated CSS is:

[data-v-d235d782] {
  --var-txt-color: #c1d32f;
}

I think I like this method more.

One workaround is to define them under a non-scoped style element like the following. However one thing to note here is, these variables will be exposed to other Vue components as well.

<style>
  :root {
     --var-txt-color: #c1d32f;
  }
</style>

<style scoped>
  #test {
     color: var(--var-txt-color);
  }
</style> 

Well, now you can use CSS variable injection.

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

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

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

Those styles are also both reactive and scoped. There won't be any unintended inheritance issues. Vue manages the CSS variables for you.

You can take a look at the RFC here.

Late answer - Here is a working example with css vars derived from standard vue structures.

<template>
  <div>
  <component :is="'style'">
    :root {
    --color: {{ color }};
    --text-decoration: {{ textDecoration }};
    --font-size: {{ fontSize }};
    }
  </component>
    <p>example</p>
  </div>
</template>

<script>

export default {

  props:{
    color: {
      type: String,
      default: '#990000'
    }
  },

  data: function () {
    return {
      textDecoration: 'underline'
    }
  },

  computed: {
    fontSize: function (){
      return Math.round(Math.random() * (5 - 1) + 1) + 'em';
    }
  }
}
</script>

<style>
p{
  color: var(--color);
  text-decoration: var(--text-decoration);
  font-size: var(--font-size);
}
</style>

Starting from the top...

  • Vue must have 1 root element, so I needed a div tag in order to include a sample p tag. However, you can just use the component-is-style tag and get rid of the div and p tags. Note the need for extra quotations "'style'".
  • The normal vue style tag can be scoped or not - as needed.
Related