How to share SASS variables with JavaScript code in VueJS
Tags: vue, css, webpack
Published: 2020-09-28 13:53:51

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](https://developer.mozilla.org/en-US/docs/Web/CSS/--*). 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`](https://github.com/css-modules/icss#export) with a webpack loader [CSS Loader](https://github.com/webpack-contrib/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:
 
```css
$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. 

```javascript
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](https://github.com/vuejs/vue-loader#how-it-works) 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:

```javascript
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:
```vue
<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"[<sup>1</sup>](#fn1).

**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.

<hr>

<small>
1. 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](https://modo.md) we can't miss a chance to drop an R.E.M. reference if we can :grimacing:
1. <span>Cover Photo by <a href="https://unsplash.com/@aznbokchoy?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Lucas Benjamin</a> on <a href="https://unsplash.com/s/photos/abstract?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>
</small>