Having a single variable declaration that works across different coding languages is a goal every developer strives to reach, no matter the technology or framework they adopt. It brings together convenience (update the value once, see it working everywhere) with consistency and lower runtime error rates.
This post explains how to share variables between Sass and JavaScript in VueJS. It will be useful every time you have layout constraints that need to be shared with front-end logic. The opposite case, where business logic determines styles update, is mostly solved with inline styles on DOM elements and CSS Custom Properties. CSS Custom Properties are well supported, but if you are looking for a solution based on SASS, keep on reading.
Step 1: Export variables from SASS/SCSS
We can use a feature borrowed from CSS Modules to export SASS variables and make them available as imports to JavaScript code. We combine a syntax :export
with a webpack loader CSS Loader that provides the exported values to JavaScript imports.
:export
is implemented as a pseudo-selector and as such is completely transparent to SASS:
$your-sass-var: #663399;
:export {
variablename: $your-sass-var;
}
This declares an SCSS variable and exports it under the variablename
identifier.
Step 2: Import SASS/SCSS variables in JavaScript
The easiest way to import your exported SASS variables is to import the SASS file itself.
import exportedVars from './path/to/file.scss'
But with Vue, we rarely author styles as independent SCSS or SASS files. Most of the time you’d author Single File Components, a custom VueJS file format that uses HTML-like syntax to describe a Vue component, usually including a template
, a script
and a style
block.
So how do you import only the style code block from a SFC? It took me a while to find the answer, but the hint is "the same way webpack does". You ask Vue Loader to do it for you.
Vue Loader turns a single file into an export of three (or more) files. In doing so, it rewrites the source filename with a syntax that allows to address a specific block. Like this:
import exportedVars from './path/to/file.vue?vue&type=style&index=0&lang=scss&module=1'
./path/to/file.vue
is your usual reference to the target file. If you are importing from the style block within the same SFC, use ./yourcomponentname.vue
. Next is the ?vue
query string part. This allows Vue Loader to understand it has already destructured the SFC into parts and should provide the code block based on its type and index. &type=style
, &index=0
and &lang=scss
allow Vue Loader to fetch the correct block and pass it to the correct preprocessor.
The index
parameter allows to address multiple style
blocks that might be in an SFC.
Lastly, more recent versions of vue-loader strictly enforce exports for CSS modules only. Appending &module=1
provides a default export when self importing our styles.
Step 3: Use your imported variables
Sass variables are accessible as properties of the identifier used for import, e.g. identifier.variablename
:
Here is a complete (and simplicistic) full example:
<template>
<div>
<p>Andy Kaufman {{ lyrics }}</p>
</div>
</template>
<script>
import styles from './ManOnTheMoon.vue?vue&type=style&index=0&lang=scss&module=1'
export default {
name: 'ManOnTheMoon',
data() {
return {
lyrics: styles.lyrics,
}
},
}
</script>
<style lang="scss">
$lyrics: ' in the wrestling match';
:export {
lyrics: unquote($lyrics);
}
</style>
Which will display the phrase: "Andy Kaufman in the wrestling match"1.
Note: that all variables are exported as strings. Use type casting and conversion if you need to coerce a value to number or boolean.
Wrapping up
This article focuses on VueJS, but this approach is valid with any framework or build pipeline that relies on webpack with css-loader.
CSS Custom Properties (AKA CSS Vars) provide an even smoother implementation: you can read and update custom properties via element.style.getPropertyValue()
and element.style.setProperty()
respectively, and I encourage you to make the switch and adopt CSS Custom Properties in place of SASS vars (even within SASS!). Yet, this solution can be useful if you already have a fully developed project built on SASS variables where refactoring to CSS Custom Properties is not an option.
- If you caught the reference in the example, I suspect that you’ll be singing "Yeah, yeah, yeah, yeah" in your head by now. At modo we can't miss a chance to drop an R.E.M. reference if we can 😬
- Cover Photo by Lucas Benjamin on Unsplash
Top comments (2)
I get "SassError: This file is already being loaded." because I want to use variables that I already imported globally in config's additionalData... so how do I find a reference? Thx
I didn't catch this, sorry for the late reply. Sass files added via the
additionalData
config option will not be made available to your JavaScript context even if you:export
your sass variables. In order to inject variables in the JavaScript context, you need to explicitly import the sass file that declares and exports them.You may need to set
css.requireModuleExtension
tofalse
in yourvue.config.js
.